im-nobsidian 0.1.6 → 0.1.7

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
@@ -8,9 +8,69 @@ import { Command } from "commander";
8
8
  import { ConfigManager, NotionClient } from "@im-nobsidian/core";
9
9
  import { input, confirm } from "@inquirer/prompts";
10
10
  import ora from "ora";
11
+ import chalk2 from "chalk";
12
+
13
+ // src/utils/format.ts
14
+ import chalk from "chalk";
15
+ var icons = {
16
+ create: chalk.green("+"),
17
+ update: chalk.yellow("~"),
18
+ delete: chalk.red("-"),
19
+ success: chalk.green("\u2713"),
20
+ fail: chalk.red("\u2715"),
21
+ conflict: chalk.magenta("!"),
22
+ synced: chalk.green("\u25CF"),
23
+ modified: chalk.yellow("\u25CF"),
24
+ newFile: chalk.cyan("\u25CF"),
25
+ conflictDot: chalk.magenta("\u25CF")
26
+ };
27
+ function header(text) {
28
+ return chalk.bold(text);
29
+ }
30
+ function separator(width = 35) {
31
+ return chalk.dim("\u2501".repeat(width));
32
+ }
33
+ function operationIcon(op) {
34
+ return icons[op];
35
+ }
36
+ function operationLabel(op) {
37
+ switch (op) {
38
+ case "create":
39
+ return chalk.green("created");
40
+ case "update":
41
+ return chalk.yellow("updated");
42
+ case "delete":
43
+ return chalk.red("deleted");
44
+ }
45
+ }
46
+ function summary(created, updated, deleted) {
47
+ const parts = [];
48
+ if (created > 0) parts.push(chalk.green(`${created} created`));
49
+ if (updated > 0) parts.push(chalk.yellow(`${updated} updated`));
50
+ if (deleted > 0) parts.push(chalk.red(`${deleted} deleted`));
51
+ if (parts.length === 0) parts.push(chalk.dim("no changes"));
52
+ return parts.join(" ");
53
+ }
54
+ function duration(ms) {
55
+ return chalk.dim(`Done in ${(ms / 1e3).toFixed(1)}s`);
56
+ }
57
+ function progress(current, total) {
58
+ return chalk.dim(`[${current}/${total}]`);
59
+ }
60
+ function dimText(text) {
61
+ return chalk.dim(text);
62
+ }
63
+ function failedItem(path, error) {
64
+ console.log(` ${icons.fail} ${path}: ${chalk.red(error)}`);
65
+ }
66
+
67
+ // src/commands/init.ts
11
68
  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").option("--non-interactive", "\uBE44\uB300\uD654\uD615 \uBAA8\uB4DC").action(async (options) => {
12
69
  const cwd = process.cwd();
13
70
  const configManager = new ConfigManager(cwd);
71
+ console.log(`
72
+ ${header(chalk2.cyan(" Im-Nobsidian Setup"))}`);
73
+ console.log(` ${separator()}`);
14
74
  if (options.nonInteractive) {
15
75
  if (!options.token || !options.rootPageId) {
16
76
  console.error("\uBE44\uB300\uD654\uD615 \uBAA8\uB4DC\uC5D0\uC11C\uB294 --token\uACFC --root-page-id\uAC00 \uD544\uC218\uC785\uB2C8\uB2E4.");
@@ -26,11 +86,16 @@ var initCommand = new Command("init").description("Im-Nobsidian \uCD08\uAE30 \uC
26
86
  try {
27
87
  const client = new NotionClient({ token: options.token });
28
88
  await client.search({ pageSize: 1, filter: { property: "object", value: "page" } });
29
- spinner2.succeed("Notion \uC5F0\uACB0 \uC131\uACF5");
89
+ spinner2.stop();
90
+ console.log(` ${icons.success} Token validated`);
30
91
  await configManager.init({ token: options.token, rootPageId: options.rootPageId });
31
- console.log("\n\u2713 Im-Nobsidian \uCD08\uAE30\uD654 \uC644\uB8CC!");
32
- console.log(` \uC124\uC815: ${configManager.configPath}`);
33
- console.log(` DB: ${configManager.dbPath}`);
92
+ console.log(`
93
+ ${icons.success} Config saved to ${dimText(configManager.configPath)}`);
94
+ console.log(` ${icons.success} State DB initialized`);
95
+ console.log(
96
+ `
97
+ ${header(chalk2.green("Ready!"))} Run ${chalk2.cyan("nobsi sync")} to start syncing.`
98
+ );
34
99
  } catch (error) {
35
100
  spinner2.fail("Notion \uC5F0\uACB0 \uC2E4\uD328");
36
101
  console.error(error instanceof Error ? error.message : error);
@@ -47,7 +112,7 @@ var initCommand = new Command("init").description("Im-Nobsidian \uCD08\uAE30 \uC
47
112
  if (!overwrite) return;
48
113
  }
49
114
  const token = await input({
50
- message: "Notion Internal Integration Token:",
115
+ message: "Notion Integration Token:",
51
116
  validate: (v) => v.startsWith("ntn_") || "ntn_\uC73C\uB85C \uC2DC\uC791\uD558\uB294 \uD1A0\uD070\uC744 \uC785\uB825\uD558\uC138\uC694"
52
117
  });
53
118
  const spinner = ora("Notion \uC5F0\uACB0 \uD655\uC778 \uC911...").start();
@@ -57,28 +122,33 @@ var initCommand = new Command("init").description("Im-Nobsidian \uCD08\uAE30 \uC
57
122
  pageSize: 10,
58
123
  filter: { property: "object", value: "page" }
59
124
  });
60
- spinner.succeed("Notion \uC5F0\uACB0 \uC131\uACF5");
125
+ spinner.stop();
126
+ console.log(` ${icons.success} Token validated`);
61
127
  if (result.results.length === 0) {
62
- spinner.warn("\uC811\uADFC \uAC00\uB2A5\uD55C \uD398\uC774\uC9C0\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. Integration\uC5D0 \uD398\uC774\uC9C0\uB97C \uACF5\uC720\uD574\uC8FC\uC138\uC694.");
128
+ console.log(
129
+ `
130
+ ${chalk2.yellow("\u26A0")} \uC811\uADFC \uAC00\uB2A5\uD55C \uD398\uC774\uC9C0\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. Integration\uC5D0 \uD398\uC774\uC9C0\uB97C \uACF5\uC720\uD574\uC8FC\uC138\uC694.`
131
+ );
63
132
  return;
64
133
  }
65
- console.log("\n\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uB8E8\uD2B8 \uD398\uC774\uC9C0:");
134
+ console.log(`
135
+ Select root page:`);
66
136
  for (const [i, page] of result.results.entries()) {
67
137
  const title = extractPageTitle(page);
68
- console.log(` [${i + 1}] ${title}`);
138
+ console.log(` ${dimText(`${i + 1}.`)} ${title}`);
69
139
  }
70
140
  const rootPageId = await input({
71
141
  message: "\uB8E8\uD2B8 \uD398\uC774\uC9C0 ID (\uC704\uC5D0\uC11C \uC120\uD0DD\uD558\uAC70\uB098 \uC9C1\uC811 \uC785\uB825):",
72
142
  default: result.results[0]?.id
73
143
  });
74
144
  await configManager.init({ token, rootPageId });
75
- console.log("\n\u2713 Im-Nobsidian \uCD08\uAE30\uD654 \uC644\uB8CC!");
76
- console.log(` \uC124\uC815: ${configManager.configPath}`);
77
- console.log(` DB: ${configManager.dbPath}`);
78
- console.log("\n\uB2E4\uC74C \uBA85\uB839\uC73C\uB85C \uB3D9\uAE30\uD654\uB97C \uC2DC\uC791\uD558\uC138\uC694:");
79
- console.log(" nobsi pull \u2014 Notion \u2192 \uB85C\uCEEC");
80
- console.log(" nobsi push \u2014 \uB85C\uCEEC \u2192 Notion");
81
- console.log(" nobsi sync \u2014 \uC591\uBC29\uD5A5");
145
+ console.log(`
146
+ ${icons.success} Config saved to ${dimText(configManager.configPath)}`);
147
+ console.log(` ${icons.success} State DB initialized`);
148
+ console.log(
149
+ `
150
+ ${header(chalk2.green("Ready!"))} Run ${chalk2.cyan("nobsi sync")} to start syncing.`
151
+ );
82
152
  } catch (error) {
83
153
  spinner.fail("Notion \uC5F0\uACB0 \uC2E4\uD328");
84
154
  console.error(error instanceof Error ? error.message : error);
@@ -106,7 +176,7 @@ import {
106
176
  SyncOrchestrator,
107
177
  NodeVaultFS
108
178
  } from "@im-nobsidian/core";
109
- import ora2 from "ora";
179
+ import chalk3 from "chalk";
110
180
  var pushCommand = new Command2("push").description("\uB85C\uCEEC \uBCC0\uACBD\uC0AC\uD56D\uC744 Notion\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 push").action(async (options) => {
111
181
  const cwd = process.cwd();
112
182
  const configManager = new ConfigManager2(cwd);
@@ -120,25 +190,30 @@ var pushCommand = new Command2("push").description("\uB85C\uCEEC \uBCC0\uACBD\uC
120
190
  });
121
191
  const vaultFs = new NodeVaultFS(cwd, config.paths);
122
192
  const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);
123
- const spinner = ora2("Push \uC911...").start();
193
+ console.log(`
194
+ ${header(" Pushing to Notion...")}`);
195
+ if (options.dryRun) console.log(dimText(" (dry-run mode)"));
196
+ console.log("");
124
197
  const result = await orchestrator.push({
125
198
  dryRun: options.dryRun,
126
199
  paths: options.path,
127
- onProgress: (current, total, path) => {
128
- const name = path.split("/").pop() ?? path;
129
- spinner.text = `Push \uC911... [${current}/${total}] ${name}`;
200
+ onProgress: (current, total, item) => {
201
+ const icon = operationIcon(item.operation);
202
+ const label = operationLabel(item.operation);
203
+ const prog = progress(current, total);
204
+ console.log(` ${icon} ${item.path} ${prog} ${label}`);
130
205
  }
131
206
  });
132
- spinner.stop();
133
207
  console.log(`
134
- \u2713 Push \uC644\uB8CC (${(result.duration / 1e3).toFixed(1)}s)`);
135
- console.log(` \uC0DD\uC131: ${result.created}`);
136
- console.log(` \uC218\uC815: ${result.updated}`);
137
- console.log(` \uC0AD\uC81C: ${result.deleted}`);
208
+ ${header(chalk3.green("Push complete"))}`);
209
+ console.log(
210
+ ` ${summary(result.created, result.updated, result.deleted)} ${dimText(`${result.failed.length} failed`)}`
211
+ );
212
+ console.log(` ${duration(result.duration)}`);
138
213
  if (result.failed.length > 0) {
139
- console.log(` \uC2E4\uD328: ${result.failed.length}`);
214
+ console.log("");
140
215
  for (const f of result.failed) {
141
- console.log(` \u2715 ${f.path}: ${f.error}`);
216
+ failedItem(f.path, f.error);
142
217
  }
143
218
  }
144
219
  } finally {
@@ -155,7 +230,7 @@ import {
155
230
  SyncOrchestrator as SyncOrchestrator2,
156
231
  NodeVaultFS as NodeVaultFS2
157
232
  } from "@im-nobsidian/core";
158
- import ora3 from "ora";
233
+ import chalk4 from "chalk";
159
234
  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").action(async (options) => {
160
235
  const cwd = process.cwd();
161
236
  const configManager = new ConfigManager3(cwd);
@@ -169,28 +244,48 @@ var pullCommand = new Command3("pull").description("Notion \uBCC0\uACBD\uC0AC\uD
169
244
  });
170
245
  const vaultFs = new NodeVaultFS2(cwd, config.paths);
171
246
  const orchestrator = new SyncOrchestrator2(config, stateDb, client, vaultFs);
172
- const spinner = ora3("Pull \uC911...").start();
247
+ console.log(`
248
+ ${header(" Pulling from Notion...")}`);
249
+ if (options.dryRun) console.log(dimText(" (dry-run mode)"));
250
+ console.log("");
173
251
  const result = await orchestrator.pull({
174
252
  dryRun: options.dryRun,
175
253
  paths: options.path,
176
- onProgress: (current, total, _pageId) => {
177
- spinner.text = `Pull \uC911... [${current}/${total}]`;
254
+ onProgress: (current, total, item) => {
255
+ const icon = operationIcon(item.operation);
256
+ const label = operationLabel(item.operation);
257
+ const prog = progress(current, total);
258
+ console.log(` ${icon} ${item.path} ${prog} ${label}`);
178
259
  }
179
260
  });
180
- spinner.stop();
261
+ if (result.imageCount > 0) {
262
+ console.log(
263
+ `
264
+ ${icons.success} ${result.imageCount} images \u2192 ${dimText("attachments/")}`
265
+ );
266
+ }
267
+ if (result.fileCount > 0) {
268
+ console.log(` ${icons.success} ${result.fileCount} files \u2192 ${dimText("attachments/")}`);
269
+ }
270
+ if (result.linkCount > 0) {
271
+ console.log(` ${icons.success} ${result.linkCount} links resolved`);
272
+ }
181
273
  console.log(`
182
- \u2713 Pull \uC644\uB8CC (${(result.duration / 1e3).toFixed(1)}s)`);
183
- console.log(` \uC0DD\uC131: ${result.created}`);
184
- console.log(` \uC218\uC815: ${result.updated}`);
185
- console.log(` \uC0AD\uC81C: ${result.deleted}`);
274
+ ${header(chalk4.green("Pull complete"))}`);
275
+ console.log(
276
+ ` ${summary(result.created, result.updated, result.deleted)} ${dimText(`${result.failed.length} failed`)}`
277
+ );
278
+ console.log(` ${duration(result.duration)}`);
186
279
  if (result.conflicts.length > 0) {
187
- console.log(` \uCDA9\uB3CC: ${result.conflicts.length}`);
188
- console.log(' "nobsi resolve"\uB85C \uCDA9\uB3CC\uC744 \uD574\uACB0\uD558\uC138\uC694.');
280
+ console.log(
281
+ `
282
+ ${icons.conflict} ${chalk4.magenta(`${result.conflicts.length} conflicts`)} \u2014 run ${chalk4.cyan("nobsi resolve")}`
283
+ );
189
284
  }
190
285
  if (result.failed.length > 0) {
191
- console.log(` \uC2E4\uD328: ${result.failed.length}`);
286
+ console.log("");
192
287
  for (const f of result.failed) {
193
- console.log(` \u2715 ${f.path}: ${f.error}`);
288
+ failedItem(f.path, f.error);
194
289
  }
195
290
  }
196
291
  } finally {
@@ -207,7 +302,7 @@ import {
207
302
  SyncOrchestrator as SyncOrchestrator3,
208
303
  NodeVaultFS as NodeVaultFS3
209
304
  } from "@im-nobsidian/core";
210
- import ora4 from "ora";
305
+ import chalk5 from "chalk";
211
306
  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").action(async (options) => {
212
307
  const cwd = process.cwd();
213
308
  const configManager = new ConfigManager4(cwd);
@@ -221,26 +316,51 @@ var syncCommand = new Command4("sync").description("\uC591\uBC29\uD5A5 \uB3D9\uA
221
316
  });
222
317
  const vaultFs = new NodeVaultFS3(cwd, config.paths);
223
318
  const orchestrator = new SyncOrchestrator3(config, stateDb, client, vaultFs);
224
- const spinner = ora4("\uB3D9\uAE30\uD654 \uC911...").start();
225
- const result = await orchestrator.sync({
319
+ console.log(`
320
+ ${header(" Bidirectional Sync")}`);
321
+ console.log(` ${separator()}`);
322
+ if (options.dryRun) console.log(dimText(" (dry-run mode)"));
323
+ console.log(`
324
+ ${header(chalk5.blue("\u25BC Pull"))} ${dimText("(Notion \u2192 Obsidian)")}`);
325
+ const pullResult = await orchestrator.pull({
226
326
  dryRun: options.dryRun,
227
- onProgress: (current, total, _item) => {
228
- spinner.text = `\uB3D9\uAE30\uD654 \uC911... [${current}/${total}]`;
327
+ onProgress: (_current, _total, item) => {
328
+ const icon = operationIcon(item.operation);
329
+ const label = operationLabel(item.operation);
330
+ console.log(` ${icon} ${item.path} ${label}`);
229
331
  }
230
332
  });
231
- spinner.stop();
333
+ if (pullResult.imageCount > 0) {
334
+ console.log(` ${icons.success} ${pullResult.imageCount} images downloaded`);
335
+ }
336
+ if (pullResult.linkCount > 0) {
337
+ console.log(` ${icons.success} ${pullResult.linkCount} links resolved`);
338
+ }
232
339
  console.log(`
233
- \u2713 \uB3D9\uAE30\uD654 \uC644\uB8CC (${(result.duration / 1e3).toFixed(1)}s)`);
340
+ ${header(chalk5.magenta("\u25B2 Push"))} ${dimText("(Obsidian \u2192 Notion)")}`);
341
+ const pushResult = await orchestrator.push({
342
+ dryRun: options.dryRun,
343
+ onProgress: (_current, _total, item) => {
344
+ const icon = operationIcon(item.operation);
345
+ const label = operationLabel(item.operation);
346
+ console.log(` ${icon} ${item.path} ${label}`);
347
+ }
348
+ });
349
+ const totalDuration = pullResult.duration + pushResult.duration;
350
+ console.log(`
351
+ ${header(chalk5.green("Sync complete"))}`);
234
352
  console.log(
235
- ` Pull: +${result.pull.created} ~${result.pull.updated} -${result.pull.deleted}`
353
+ ` ${chalk5.blue("Pull:")} ${summary(pullResult.created, pullResult.updated, pullResult.deleted)}`
236
354
  );
237
355
  console.log(
238
- ` Push: +${result.push.created} ~${result.push.updated} -${result.push.deleted}`
356
+ ` ${chalk5.magenta("Push:")} ${summary(pushResult.created, pushResult.updated, pushResult.deleted)}`
239
357
  );
240
- if (result.conflicts.length > 0) {
241
- console.log(`
242
- \u26A0 \uCDA9\uB3CC ${result.conflicts.length}\uAC74`);
243
- console.log(' "nobsi resolve"\uB85C \uCDA9\uB3CC\uC744 \uD574\uACB0\uD558\uC138\uC694.');
358
+ console.log(` ${duration(totalDuration)}`);
359
+ if (pullResult.conflicts.length > 0) {
360
+ console.log(
361
+ `
362
+ ${icons.conflict} ${chalk5.magenta(`${pullResult.conflicts.length} conflicts`)} \u2014 run ${chalk5.cyan("nobsi resolve")}`
363
+ );
244
364
  }
245
365
  } finally {
246
366
  stateDb.close();
@@ -256,6 +376,7 @@ import {
256
376
  SyncOrchestrator as SyncOrchestrator4,
257
377
  NodeVaultFS as NodeVaultFS4
258
378
  } from "@im-nobsidian/core";
379
+ import chalk6 from "chalk";
259
380
  var statusCommand = new Command5("status").description("\uB3D9\uAE30\uD654 \uC0C1\uD0DC \uD655\uC778").action(async () => {
260
381
  const cwd = process.cwd();
261
382
  const configManager = new ConfigManager5(cwd);
@@ -269,28 +390,83 @@ var statusCommand = new Command5("status").description("\uB3D9\uAE30\uD654 \uC0C
269
390
  const vaultFs = new NodeVaultFS4(cwd, config.paths);
270
391
  const orchestrator = new SyncOrchestrator4(config, stateDb, client, vaultFs);
271
392
  const status = await orchestrator.status();
393
+ console.log(`
394
+ ${header(" Sync Status")}`);
395
+ console.log(` ${separator(50)}`);
396
+ const rootId = config.notion.rootPageId;
397
+ const direction = config.sync.direction === "both" ? "bidirectional" : config.sync.direction;
398
+ console.log(` ${dimText("Root page:")} ${rootId.slice(0, 8)}...`);
399
+ console.log(` ${dimText("Direction:")} ${direction}`);
272
400
  if (status.lastSyncAt) {
273
- console.log(`\uB9C8\uC9C0\uB9C9 \uB3D9\uAE30\uD654: ${status.lastSyncAt}`);
401
+ const date = new Date(status.lastSyncAt);
402
+ const iso = date.getFullYear() + "-" + String(date.getMonth() + 1).padStart(2, "0") + "-" + String(date.getDate()).padStart(2, "0") + " " + String(date.getHours()).padStart(2, "0") + ":" + String(date.getMinutes()).padStart(2, "0") + ":" + String(date.getSeconds()).padStart(2, "0");
403
+ console.log(` ${dimText("Last sync:")} ${iso}`);
274
404
  } else {
275
- console.log("\uC544\uC9C1 \uB3D9\uAE30\uD654\uB41C \uC801 \uC5C6\uC74C");
405
+ console.log(` ${dimText("Last sync:")} ${chalk6.yellow("never")}`);
276
406
  }
277
- console.log("");
278
- if (status.localChanges.length === 0) {
279
- console.log("\uBCC0\uACBD\uC0AC\uD56D \uC5C6\uC74C (clean)");
280
- } else {
281
- console.log(`\uB85C\uCEEC \uBCC0\uACBD\uC0AC\uD56D (${status.localChanges.length}\uAC74):`);
282
- for (const change of status.localChanges) {
283
- const prefix = change.type === "created" ? "+" : change.type === "deleted" ? "-" : "~";
284
- console.log(` ${prefix} ${change.path}`);
407
+ const allRecords = stateDb.getAll();
408
+ const total = allRecords.length;
409
+ const synced = allRecords.filter((r) => r.status === "synced").length;
410
+ const conflictCount = status.conflictRecords.length;
411
+ const createdChanges = status.localChanges.filter((c) => c.type === "created");
412
+ const modifiedChanges = status.localChanges.filter((c) => c.type === "modified");
413
+ console.log(`
414
+ ${header(`Tracked files: ${total}`)}`);
415
+ console.log(` ${icons.synced} ${chalk6.green("synced")} ${synced}`);
416
+ if (modifiedChanges.length > 0) {
417
+ console.log(` ${icons.modified} ${chalk6.yellow("modified")} ${modifiedChanges.length}`);
418
+ }
419
+ if (createdChanges.length > 0) {
420
+ console.log(` ${icons.newFile} ${chalk6.cyan("new")} ${createdChanges.length}`);
421
+ }
422
+ if (conflictCount > 0) {
423
+ console.log(` ${icons.conflictDot} ${chalk6.magenta("conflict")} ${conflictCount}`);
424
+ }
425
+ if (modifiedChanges.length > 0) {
426
+ console.log(`
427
+ ${header("Modified files:")}`);
428
+ for (const c of modifiedChanges.slice(0, 10)) {
429
+ console.log(
430
+ ` ${chalk6.yellow("~")} ${c.path.padEnd(30)} ${dimText("(local changed)")}`
431
+ );
432
+ }
433
+ if (modifiedChanges.length > 10) {
434
+ console.log(dimText(` ... and ${modifiedChanges.length - 10} more`));
285
435
  }
286
436
  }
287
- if (status.conflictRecords.length > 0) {
437
+ if (createdChanges.length > 0) {
288
438
  console.log(`
289
- \uCDA9\uB3CC (${status.conflictRecords.length}\uAC74):`);
290
- for (const record of status.conflictRecords) {
291
- console.log(` \u26A0 ${record.obsidianPath}`);
439
+ ${header("New files:")}`);
440
+ for (const c of createdChanges.slice(0, 10)) {
441
+ console.log(` ${chalk6.cyan("+")} ${c.path.padEnd(30)} ${dimText("(untracked)")}`);
442
+ }
443
+ if (createdChanges.length > 10) {
444
+ console.log(dimText(` ... and ${createdChanges.length - 10} more`));
292
445
  }
293
446
  }
447
+ if (conflictCount > 0) {
448
+ console.log(`
449
+ ${header(chalk6.magenta("Conflicts:"))}`);
450
+ for (const r of status.conflictRecords) {
451
+ console.log(
452
+ ` ${icons.conflict} ${r.obsidianPath.padEnd(30)} ${dimText("(both sides changed)")}`
453
+ );
454
+ }
455
+ }
456
+ if (status.localChanges.length > 0 || conflictCount > 0) {
457
+ console.log("");
458
+ if (conflictCount > 0) {
459
+ console.log(
460
+ ` ${dimText("Run")} ${chalk6.cyan("nobsi resolve")} ${dimText("to resolve conflicts")}`
461
+ );
462
+ }
463
+ console.log(
464
+ ` ${dimText("Run")} ${chalk6.cyan("nobsi sync")} ${dimText("to push/pull changes")}`
465
+ );
466
+ } else {
467
+ console.log(`
468
+ ${chalk6.green("Everything up to date")} \u2713`);
469
+ }
294
470
  } finally {
295
471
  stateDb.close();
296
472
  }
@@ -573,5 +749,7 @@ program.addCommand(statusCommand);
573
749
  program.addCommand(diffCommand);
574
750
  program.addCommand(resolveCommand);
575
751
  program.addCommand(watchCommand);
576
- program.parse();
752
+ program.parseAsync().then(() => {
753
+ setTimeout(() => process.exit(0), 100);
754
+ });
577
755
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/commands/push.ts","../src/commands/pull.ts","../src/commands/sync.ts","../src/commands/status.ts","../src/commands/diff.ts","../src/commands/resolve.ts","../src/commands/watch.ts"],"sourcesContent":["import { 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 { resolveCommand } from \"./commands/resolve.js\";\nimport { watchCommand } from \"./commands/watch.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"im-nobsidian\")\n .description(\"Im-Nobsidian: Obsidian ↔ Notion 양방향 동기화 CLI\")\n .version(\"0.1.1\")\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(resolveCommand);\nprogram.addCommand(watchCommand);\n\nprogram.parse();\n","import { Command } from \"commander\";\nimport { ConfigManager, NotionClient } from \"@im-nobsidian/core\";\nimport { input, confirm } from \"@inquirer/prompts\";\nimport ora from \"ora\";\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 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(\"ntn_\")) {\n console.error(\"토큰은 ntn_으로 시작해야 합니다.\");\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.succeed(\"Notion 연결 성공\");\n\n await configManager.init({ token: options.token, rootPageId: options.rootPageId });\n console.log(\"\\n✓ Im-Nobsidian 초기화 완료!\");\n console.log(` 설정: ${configManager.configPath}`);\n console.log(` DB: ${configManager.dbPath}`);\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 Internal Integration Token:\",\n validate: (v) => v.startsWith(\"ntn_\") || \"ntn_으로 시작하는 토큰을 입력하세요\",\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.succeed(\"Notion 연결 성공\");\n\n if (result.results.length === 0) {\n spinner.warn(\"접근 가능한 페이지가 없습니다. Integration에 페이지를 공유해주세요.\");\n return;\n }\n\n console.log(\"\\n사용 가능한 루트 페이지:\");\n for (const [i, page] of result.results.entries()) {\n const title = extractPageTitle(page);\n console.log(` [${i + 1}] ${title}`);\n }\n\n const rootPageId = await input({\n message: \"루트 페이지 ID (위에서 선택하거나 직접 입력):\",\n default: result.results[0]?.id,\n });\n\n await configManager.init({ token, rootPageId });\n console.log(\"\\n✓ Im-Nobsidian 초기화 완료!\");\n console.log(` 설정: ${configManager.configPath}`);\n console.log(` DB: ${configManager.dbPath}`);\n console.log(\"\\n다음 명령으로 동기화를 시작하세요:\");\n console.log(\" nobsi pull — Notion → 로컬\");\n console.log(\" nobsi push — 로컬 → Notion\");\n console.log(\" nobsi sync — 양방향\");\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 { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport ora from \"ora\";\n\nexport const pushCommand = new Command(\"push\")\n .description(\"로컬 변경사항을 Notion에 반영\")\n .option(\"--dry-run\", \"실제 반영 없이 변경사항만 표시\")\n .option(\"-p, --path <paths...>\", \"특정 경로만 push\")\n .action(async (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 = new NotionClient({\n token: config.notion.token,\n concurrency: config.advanced.concurrency,\n timeoutMs: config.advanced.timeoutMs,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const spinner = ora(\"Push 중...\").start();\n const result = await orchestrator.push({\n dryRun: options.dryRun,\n paths: options.path,\n onProgress: (current, total, path) => {\n const name = path.split(\"/\").pop() ?? path;\n spinner.text = `Push 중... [${current}/${total}] ${name}`;\n },\n });\n spinner.stop();\n\n console.log(`\\n✓ Push 완료 (${(result.duration / 1000).toFixed(1)}s)`);\n console.log(` 생성: ${result.created}`);\n console.log(` 수정: ${result.updated}`);\n console.log(` 삭제: ${result.deleted}`);\n\n if (result.failed.length > 0) {\n console.log(` 실패: ${result.failed.length}`);\n for (const f of result.failed) {\n console.log(` ✕ ${f.path}: ${f.error}`);\n }\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 ora from \"ora\";\n\nexport const pullCommand = new Command(\"pull\")\n .description(\"Notion 변경사항을 로컬에 반영\")\n .option(\"--dry-run\", \"실제 반영 없이 변경사항만 표시\")\n .option(\"-p, --path <paths...>\", \"특정 경로만 pull\")\n .action(async (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 = new NotionClient({\n token: config.notion.token,\n concurrency: config.advanced.concurrency,\n timeoutMs: config.advanced.timeoutMs,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const spinner = ora(\"Pull 중...\").start();\n const result = await orchestrator.pull({\n dryRun: options.dryRun,\n paths: options.path,\n onProgress: (current, total, _pageId) => {\n spinner.text = `Pull 중... [${current}/${total}]`;\n },\n });\n spinner.stop();\n\n console.log(`\\n✓ Pull 완료 (${(result.duration / 1000).toFixed(1)}s)`);\n console.log(` 생성: ${result.created}`);\n console.log(` 수정: ${result.updated}`);\n console.log(` 삭제: ${result.deleted}`);\n\n if (result.conflicts.length > 0) {\n console.log(` 충돌: ${result.conflicts.length}`);\n console.log(' \"nobsi resolve\"로 충돌을 해결하세요.');\n }\n\n if (result.failed.length > 0) {\n console.log(` 실패: ${result.failed.length}`);\n for (const f of result.failed) {\n console.log(` ✕ ${f.path}: ${f.error}`);\n }\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 ora from \"ora\";\n\nexport const syncCommand = new Command(\"sync\")\n .description(\"양방향 동기화 (Pull → Push)\")\n .option(\"--dry-run\", \"실제 반영 없이 변경사항만 표시\")\n .action(async (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 = new NotionClient({\n token: config.notion.token,\n concurrency: config.advanced.concurrency,\n timeoutMs: config.advanced.timeoutMs,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const spinner = ora(\"동기화 중...\").start();\n const result = await orchestrator.sync({\n dryRun: options.dryRun,\n onProgress: (current, total, _item) => {\n spinner.text = `동기화 중... [${current}/${total}]`;\n },\n });\n spinner.stop();\n\n console.log(`\\n✓ 동기화 완료 (${(result.duration / 1000).toFixed(1)}s)`);\n console.log(\n ` Pull: +${result.pull.created} ~${result.pull.updated} -${result.pull.deleted}`,\n );\n console.log(\n ` Push: +${result.push.created} ~${result.push.updated} -${result.push.deleted}`,\n );\n\n if (result.conflicts.length > 0) {\n console.log(`\\n ⚠ 충돌 ${result.conflicts.length}건`);\n console.log(' \"nobsi resolve\"로 충돌을 해결하세요.');\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\";\n\nexport const statusCommand = new Command(\"status\")\n .description(\"동기화 상태 확인\")\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 = new NotionClient({\n token: config.notion.token,\n concurrency: config.advanced.concurrency,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const status = await orchestrator.status();\n\n if (status.lastSyncAt) {\n console.log(`마지막 동기화: ${status.lastSyncAt}`);\n } else {\n console.log(\"아직 동기화된 적 없음\");\n }\n\n console.log(\"\");\n\n if (status.localChanges.length === 0) {\n console.log(\"변경사항 없음 (clean)\");\n } else {\n console.log(`로컬 변경사항 (${status.localChanges.length}건):`);\n for (const change of status.localChanges) {\n const prefix = change.type === \"created\" ? \"+\" : change.type === \"deleted\" ? \"-\" : \"~\";\n console.log(` ${prefix} ${change.path}`);\n }\n }\n\n if (status.conflictRecords.length > 0) {\n console.log(`\\n충돌 (${status.conflictRecords.length}건):`);\n for (const record of status.conflictRecords) {\n console.log(` ⚠ ${record.obsidianPath}`);\n }\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 { createTwoFilesPatch } from \"diff\";\n\nexport const diffCommand = new Command(\"diff\")\n .description(\"로컬과 Notion 간의 차이 표시\")\n .argument(\"[path]\", \"특정 파일 경로 (생략 시 변경된 모든 파일)\")\n .option(\"--color\", \"컬러 출력\", true)\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 = new NotionClient({\n token: config.notion.token,\n concurrency: config.advanced.concurrency,\n timeoutMs: config.advanced.timeoutMs,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const status = await orchestrator.status();\n\n const changes = targetPath\n ? status.localChanges.filter((c) => c.path === targetPath || c.path.startsWith(targetPath))\n : status.localChanges;\n\n if (changes.length === 0) {\n console.log(\"변경사항 없음\");\n return;\n }\n\n for (const change of changes) {\n const record = stateDb.getByPath(change.path);\n\n if (change.type === \"created\") {\n const content = await vaultFs.readFile(change.path);\n const patch = createTwoFilesPatch(`/dev/null`, `b/${change.path}`, \"\", content);\n printPatch(patch, options.color);\n } else if (change.type === \"deleted\") {\n if (record?.baseSnapshot) {\n const oldContent = record.baseSnapshot.toString(\"utf-8\");\n const patch = createTwoFilesPatch(`a/${change.path}`, `/dev/null`, oldContent, \"\");\n printPatch(patch, options.color);\n } else {\n console.log(`--- a/${change.path}`);\n console.log(`+++ /dev/null`);\n console.log(`(이전 스냅샷 없음)`);\n }\n } else if (change.type === \"modified\" && record?.baseSnapshot) {\n const currentContent = await vaultFs.readFile(change.path);\n const baseContent = record.baseSnapshot.toString(\"utf-8\");\n const patch = createTwoFilesPatch(\n `a/${change.path}`,\n `b/${change.path}`,\n baseContent,\n currentContent,\n );\n printPatch(patch, options.color);\n }\n\n console.log(\"\");\n }\n } finally {\n stateDb.close();\n }\n });\n\nfunction printPatch(patch: string, color: boolean): void {\n for (const line of patch.split(\"\\n\")) {\n if (!color) {\n console.log(line);\n continue;\n }\n\n if (line.startsWith(\"+++\") || line.startsWith(\"---\")) {\n console.log(`\\x1b[1m${line}\\x1b[0m`);\n } else if (line.startsWith(\"+\")) {\n console.log(`\\x1b[32m${line}\\x1b[0m`);\n } else if (line.startsWith(\"-\")) {\n console.log(`\\x1b[31m${line}\\x1b[0m`);\n } else if (line.startsWith(\"@@\")) {\n console.log(`\\x1b[36m${line}\\x1b[0m`);\n } else {\n console.log(line);\n }\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 ConflictResolver,\n} from \"@im-nobsidian/core\";\nimport type { Conflict, ResolutionChoice } from \"@im-nobsidian/core\";\n\nexport const resolveCommand = new Command(\"resolve\")\n .description(\"충돌을 해결합니다\")\n .option(\"-s, --strategy <strategy>\", \"자동 해결 전략 (local-first, remote-first, duplicate)\")\n .action(async (options: { strategy?: string }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n\n try {\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n const client = new NotionClient({\n token: config.notion.token,\n concurrency: 3,\n timeoutMs: 30000,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n const resolver = new ConflictResolver(stateDb, vaultFs);\n\n await orchestrator.status();\n const conflictRecords = stateDb.getByStatus(\"conflict\");\n\n if (conflictRecords.length === 0) {\n console.log(\"충돌이 없습니다.\");\n stateDb.close();\n return;\n }\n\n console.log(`\\n충돌 ${conflictRecords.length}건 발견\\n`);\n\n if (options.strategy) {\n const strategy = options.strategy as \"local-first\" | \"remote-first\" | \"duplicate\";\n const shouldProceed = await confirm({\n message: `${conflictRecords.length}건의 충돌을 '${strategy}' 전략으로 일괄 해결할까요?`,\n });\n\n if (!shouldProceed) {\n stateDb.close();\n return;\n }\n\n const pullResult = await orchestrator.pull();\n const results = await resolver.resolveAll(pullResult.conflicts, strategy);\n\n for (const result of results) {\n const icon = result.success ? \"\\x1b[32m✓\\x1b[0m\" : \"\\x1b[31m✗\\x1b[0m\";\n console.log(` ${icon} ${result.path} → ${result.choice}`);\n }\n } else {\n const pullResult = await orchestrator.pull();\n\n for (const conflict of pullResult.conflicts) {\n await resolveInteractive(conflict, resolver);\n }\n }\n\n stateDb.close();\n console.log(\"\\n충돌 해결 완료\");\n } catch (error) {\n console.error(\"오류:\", error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n });\n\nasync function resolveInteractive(conflict: Conflict, resolver: ConflictResolver): Promise<void> {\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 const diff = resolver.generateDiff(conflict);\n const diffLines = diff.split(\"\\n\");\n for (const line of diffLines) {\n if (line.startsWith(\"- \")) {\n console.log(`\\x1b[31m${line}\\x1b[0m`);\n } else if (line.startsWith(\"+ \")) {\n console.log(`\\x1b[32m${line}\\x1b[0m`);\n } else if (line.startsWith(\"---\") || line.startsWith(\"+++\")) {\n console.log(`\\x1b[1m${line}\\x1b[0m`);\n } else {\n console.log(line);\n }\n }\n\n const choice = await select<ResolutionChoice>({\n message: \"어떻게 해결할까요?\",\n choices: [\n { name: \"로컬 유지 (Obsidian 버전 유지)\", value: \"local\" },\n { name: \"원격 유지 (Notion 버전으로 덮어쓰기)\", value: \"remote\" },\n { name: \"자동 병합 (3-way merge)\", value: \"merge\" },\n { name: \"복제 (원본 유지 + .conflict 파일 생성)\", value: \"duplicate\" },\n ],\n });\n\n const result = await resolver.resolve(conflict, choice);\n\n if (result.success) {\n console.log(`\\x1b[32m✓ ${path} → ${choice}로 해결\\x1b[0m`);\n } else if (result.mergeHadConflicts) {\n console.log(`\\x1b[33m⚠ ${path} → 자동 병합 완료 (수동 확인 필요한 충돌 마커 있음)\\x1b[0m`);\n } else {\n console.log(`\\x1b[31m✗ ${path} → 해결 실패\\x1b[0m`);\n }\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n WatchSyncService,\n} from \"@im-nobsidian/core\";\nimport type { SyncResult } 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 push = `Push: +${result.push.created} ~${result.push.updated} -${result.push.deleted}`;\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\nexport const watchCommand = new Command(\"watch\")\n .description(\"파일 변경 감지 + 자동 동기화\")\n .option(\"-d, --debounce <ms>\", \"디바운스 대기 시간(ms)\", \"2000\")\n .option(\"-i, --interval <seconds>\", \"풀 동기화 주기(초, 0이면 비활성)\", \"0\")\n .action(async (options: { debounce: string; interval: 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 const client = new NotionClient({\n token: config.notion.token,\n concurrency: config.advanced.concurrency,\n timeoutMs: config.advanced.timeoutMs,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const debounceMs = parseInt(options.debounce, 10);\n const intervalSec = parseInt(options.interval, 10);\n\n const service = new WatchSyncService(cwd, orchestrator, {\n debounceMs,\n onFileChange: (event, path) => {\n console.log(`[${timestamp()}] ${event}: ${path}`);\n },\n onSyncStart: () => {\n console.log(`[${timestamp()}] 동기화 시작...`);\n },\n onSyncComplete: (result) => {\n console.log(`[${timestamp()}] 동기화 완료 — ${formatResult(result)}`);\n },\n onSyncError: (error) => {\n console.error(`[${timestamp()}] 동기화 실패: ${error.message}`);\n },\n });\n\n let pullTimer: ReturnType<typeof setInterval> | null = null;\n\n const shutdown = async (): Promise<void> => {\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 pullTimer = setInterval(async () => {\n if (service.isSyncing()) return;\n try {\n console.log(`[${timestamp()}] 주기적 동기화 시작...`);\n const result = await orchestrator.sync();\n console.log(`[${timestamp()}] 주기적 동기화 완료 — ${formatResult(result)}`);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`[${timestamp()}] 주기적 동기화 실패: ${msg}`);\n }\n }, intervalSec * 1000);\n }\n\n await new Promise(() => {});\n });\n"],"mappings":"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","names":["Command","spinner","Command","ConfigManager","NotionClient","ora","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","ora","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","ora","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","confirm","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/utils/format.ts","../src/commands/push.ts","../src/commands/pull.ts","../src/commands/sync.ts","../src/commands/status.ts","../src/commands/diff.ts","../src/commands/resolve.ts","../src/commands/watch.ts"],"sourcesContent":["import { 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 { resolveCommand } from \"./commands/resolve.js\";\nimport { watchCommand } from \"./commands/watch.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"im-nobsidian\")\n .description(\"Im-Nobsidian: Obsidian ↔ Notion 양방향 동기화 CLI\")\n .version(\"0.1.1\")\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(resolveCommand);\nprogram.addCommand(watchCommand);\n\nprogram.parseAsync().then(() => {\n setTimeout(() => process.exit(0), 100);\n});\n","import { Command } from \"commander\";\nimport { ConfigManager, NotionClient } 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\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(\"ntn_\")) {\n console.error(\"토큰은 ntn_으로 시작해야 합니다.\");\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: options.rootPageId });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\n console.log(` ${icons.success} State DB initialized`);\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) => v.startsWith(\"ntn_\") || \"ntn_으로 시작하는 토큰을 입력하세요\",\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 rootPageId = await input({\n message: \"루트 페이지 ID (위에서 선택하거나 직접 입력):\",\n default: result.results[0]?.id,\n });\n\n await configManager.init({ token, rootPageId });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\n console.log(` ${icons.success} State DB initialized`);\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\";\n\nexport const icons = {\n create: chalk.green(\"+\"),\n update: chalk.yellow(\"~\"),\n delete: chalk.red(\"-\"),\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: \"create\" | \"update\" | \"delete\"): string {\n return icons[op];\n}\n\nexport function operationLabel(op: \"create\" | \"update\" | \"delete\"): 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 }\n}\n\nexport function summary(created: number, updated: number, deleted: number): 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 (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\nexport function duration(ms: number): string {\n return chalk.dim(`Done in ${(ms / 1000).toFixed(1)}s`);\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\nexport function failedItem(path: string, error: string): void {\n console.log(` ${icons.fail} ${path}: ${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 chalk from \"chalk\";\nimport {\n header,\n operationIcon,\n operationLabel,\n progress,\n summary,\n duration,\n failedItem,\n dimText,\n} from \"../utils/format.js\";\n\nexport const pushCommand = new Command(\"push\")\n .description(\"로컬 변경사항을 Notion에 반영\")\n .option(\"--dry-run\", \"실제 반영 없이 변경사항만 표시\")\n .option(\"-p, --path <paths...>\", \"특정 경로만 push\")\n .action(async (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 = new NotionClient({\n token: config.notion.token,\n concurrency: config.advanced.concurrency,\n timeoutMs: config.advanced.timeoutMs,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n console.log(`\\n${header(\" Pushing to Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n\n const result = await orchestrator.push({\n dryRun: options.dryRun,\n paths: options.path,\n onProgress: (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 console.log(`\\n ${header(chalk.green(\"Push complete\"))}`);\n console.log(\n ` ${summary(result.created, result.updated, result.deleted)} ${dimText(`${result.failed.length} failed`)}`,\n );\n console.log(` ${duration(result.duration)}`);\n\n if (result.failed.length > 0) {\n console.log(\"\");\n for (const f of result.failed) {\n failedItem(f.path, f.error);\n }\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 operationIcon,\n operationLabel,\n progress,\n summary,\n duration,\n icons,\n failedItem,\n dimText,\n} from \"../utils/format.js\";\n\nexport const pullCommand = new Command(\"pull\")\n .description(\"Notion 변경사항을 로컬에 반영\")\n .option(\"--dry-run\", \"실제 반영 없이 변경사항만 표시\")\n .option(\"-p, --path <paths...>\", \"특정 경로만 pull\")\n .action(async (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 = new NotionClient({\n token: config.notion.token,\n concurrency: config.advanced.concurrency,\n timeoutMs: config.advanced.timeoutMs,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n console.log(`\\n${header(\" Pulling from Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n\n const result = await orchestrator.pull({\n dryRun: options.dryRun,\n paths: options.path,\n onProgress: (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 (result.imageCount > 0) {\n console.log(\n `\\n ${icons.success} ${result.imageCount} images → ${dimText(\"attachments/\")}`,\n );\n }\n if (result.fileCount > 0) {\n console.log(` ${icons.success} ${result.fileCount} files → ${dimText(\"attachments/\")}`);\n }\n if (result.linkCount > 0) {\n console.log(` ${icons.success} ${result.linkCount} links resolved`);\n }\n\n console.log(`\\n ${header(chalk.green(\"Pull complete\"))}`);\n console.log(\n ` ${summary(result.created, result.updated, result.deleted)} ${dimText(`${result.failed.length} failed`)}`,\n );\n console.log(` ${duration(result.duration)}`);\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 (result.failed.length > 0) {\n console.log(\"\");\n for (const f of result.failed) {\n failedItem(f.path, f.error);\n }\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} from \"../utils/format.js\";\n\nexport const syncCommand = new Command(\"sync\")\n .description(\"양방향 동기화 (Pull → Push)\")\n .option(\"--dry-run\", \"실제 반영 없이 변경사항만 표시\")\n .action(async (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 = new NotionClient({\n token: config.notion.token,\n concurrency: config.advanced.concurrency,\n timeoutMs: config.advanced.timeoutMs,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n console.log(`\\n${header(\" Bidirectional Sync\")}`);\n console.log(` ${separator()}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n\n // Pull phase\n console.log(`\\n ${header(chalk.blue(\"▼ Pull\"))} ${dimText(\"(Notion → Obsidian)\")}`);\n\n const pullResult = await orchestrator.pull({\n dryRun: options.dryRun,\n onProgress: (_current, _total, item) => {\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n console.log(` ${icon} ${item.path} ${label}`);\n },\n });\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\n // Push phase\n console.log(`\\n ${header(chalk.magenta(\"▲ Push\"))} ${dimText(\"(Obsidian → Notion)\")}`);\n\n const pushResult = await orchestrator.push({\n dryRun: options.dryRun,\n onProgress: (_current, _total, item) => {\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n console.log(` ${icon} ${item.path} ${label}`);\n },\n });\n\n // Summary\n const totalDuration = pullResult.duration + pushResult.duration;\n console.log(`\\n ${header(chalk.green(\"Sync complete\"))}`);\n console.log(\n ` ${chalk.blue(\"Pull:\")} ${summary(pullResult.created, pullResult.updated, pullResult.deleted)}`,\n );\n console.log(\n ` ${chalk.magenta(\"Push:\")} ${summary(pushResult.created, pushResult.updated, pushResult.deleted)}`,\n );\n console.log(` ${duration(totalDuration)}`);\n\n if (pullResult.conflicts.length > 0) {\n console.log(\n `\\n ${icons.conflict} ${chalk.magenta(`${pullResult.conflicts.length} conflicts`)} — run ${chalk.cyan(\"nobsi resolve\")}`,\n );\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 { header, separator, icons, dimText } from \"../utils/format.js\";\n\nexport const statusCommand = new Command(\"status\")\n .description(\"동기화 상태 확인\")\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 = new NotionClient({\n token: config.notion.token,\n concurrency: config.advanced.concurrency,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const status = await orchestrator.status();\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 const date = new Date(status.lastSyncAt);\n const iso =\n date.getFullYear() +\n \"-\" +\n String(date.getMonth() + 1).padStart(2, \"0\") +\n \"-\" +\n String(date.getDate()).padStart(2, \"0\") +\n \" \" +\n String(date.getHours()).padStart(2, \"0\") +\n \":\" +\n String(date.getMinutes()).padStart(2, \"0\") +\n \":\" +\n String(date.getSeconds()).padStart(2, \"0\");\n console.log(` ${dimText(\"Last sync:\")} ${iso}`);\n } else {\n console.log(` ${dimText(\"Last sync:\")} ${chalk.yellow(\"never\")}`);\n }\n\n // Tracked files summary\n const allRecords = stateDb.getAll();\n const total = allRecords.length;\n const synced = allRecords.filter((r) => r.status === \"synced\").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\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 (createdChanges.length > 0) {\n console.log(` ${icons.newFile} ${chalk.cyan(\"new\")} ${createdChanges.length}`);\n }\n if (conflictCount > 0) {\n console.log(` ${icons.conflictDot} ${chalk.magenta(\"conflict\")} ${conflictCount}`);\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 // 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 // Conflicts\n if (conflictCount > 0) {\n console.log(`\\n ${header(chalk.magenta(\"Conflicts:\"))}`);\n for (const r of status.conflictRecords) {\n console.log(\n ` ${icons.conflict} ${r.obsidianPath.padEnd(30)} ${dimText(\"(both sides changed)\")}`,\n );\n }\n }\n\n // Helpful commands\n if (status.localChanges.length > 0 || conflictCount > 0) {\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 } else {\n console.log(`\\n ${chalk.green(\"Everything up to date\")} ✓`);\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 { createTwoFilesPatch } from \"diff\";\n\nexport const diffCommand = new Command(\"diff\")\n .description(\"로컬과 Notion 간의 차이 표시\")\n .argument(\"[path]\", \"특정 파일 경로 (생략 시 변경된 모든 파일)\")\n .option(\"--color\", \"컬러 출력\", true)\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 = new NotionClient({\n token: config.notion.token,\n concurrency: config.advanced.concurrency,\n timeoutMs: config.advanced.timeoutMs,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const status = await orchestrator.status();\n\n const changes = targetPath\n ? status.localChanges.filter((c) => c.path === targetPath || c.path.startsWith(targetPath))\n : status.localChanges;\n\n if (changes.length === 0) {\n console.log(\"변경사항 없음\");\n return;\n }\n\n for (const change of changes) {\n const record = stateDb.getByPath(change.path);\n\n if (change.type === \"created\") {\n const content = await vaultFs.readFile(change.path);\n const patch = createTwoFilesPatch(`/dev/null`, `b/${change.path}`, \"\", content);\n printPatch(patch, options.color);\n } else if (change.type === \"deleted\") {\n if (record?.baseSnapshot) {\n const oldContent = record.baseSnapshot.toString(\"utf-8\");\n const patch = createTwoFilesPatch(`a/${change.path}`, `/dev/null`, oldContent, \"\");\n printPatch(patch, options.color);\n } else {\n console.log(`--- a/${change.path}`);\n console.log(`+++ /dev/null`);\n console.log(`(이전 스냅샷 없음)`);\n }\n } else if (change.type === \"modified\" && record?.baseSnapshot) {\n const currentContent = await vaultFs.readFile(change.path);\n const baseContent = record.baseSnapshot.toString(\"utf-8\");\n const patch = createTwoFilesPatch(\n `a/${change.path}`,\n `b/${change.path}`,\n baseContent,\n currentContent,\n );\n printPatch(patch, options.color);\n }\n\n console.log(\"\");\n }\n } finally {\n stateDb.close();\n }\n });\n\nfunction printPatch(patch: string, color: boolean): void {\n for (const line of patch.split(\"\\n\")) {\n if (!color) {\n console.log(line);\n continue;\n }\n\n if (line.startsWith(\"+++\") || line.startsWith(\"---\")) {\n console.log(`\\x1b[1m${line}\\x1b[0m`);\n } else if (line.startsWith(\"+\")) {\n console.log(`\\x1b[32m${line}\\x1b[0m`);\n } else if (line.startsWith(\"-\")) {\n console.log(`\\x1b[31m${line}\\x1b[0m`);\n } else if (line.startsWith(\"@@\")) {\n console.log(`\\x1b[36m${line}\\x1b[0m`);\n } else {\n console.log(line);\n }\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 ConflictResolver,\n} from \"@im-nobsidian/core\";\nimport type { Conflict, ResolutionChoice } from \"@im-nobsidian/core\";\n\nexport const resolveCommand = new Command(\"resolve\")\n .description(\"충돌을 해결합니다\")\n .option(\"-s, --strategy <strategy>\", \"자동 해결 전략 (local-first, remote-first, duplicate)\")\n .action(async (options: { strategy?: string }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n\n try {\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n const client = new NotionClient({\n token: config.notion.token,\n concurrency: 3,\n timeoutMs: 30000,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n const resolver = new ConflictResolver(stateDb, vaultFs);\n\n await orchestrator.status();\n const conflictRecords = stateDb.getByStatus(\"conflict\");\n\n if (conflictRecords.length === 0) {\n console.log(\"충돌이 없습니다.\");\n stateDb.close();\n return;\n }\n\n console.log(`\\n충돌 ${conflictRecords.length}건 발견\\n`);\n\n if (options.strategy) {\n const strategy = options.strategy as \"local-first\" | \"remote-first\" | \"duplicate\";\n const shouldProceed = await confirm({\n message: `${conflictRecords.length}건의 충돌을 '${strategy}' 전략으로 일괄 해결할까요?`,\n });\n\n if (!shouldProceed) {\n stateDb.close();\n return;\n }\n\n const pullResult = await orchestrator.pull();\n const results = await resolver.resolveAll(pullResult.conflicts, strategy);\n\n for (const result of results) {\n const icon = result.success ? \"\\x1b[32m✓\\x1b[0m\" : \"\\x1b[31m✗\\x1b[0m\";\n console.log(` ${icon} ${result.path} → ${result.choice}`);\n }\n } else {\n const pullResult = await orchestrator.pull();\n\n for (const conflict of pullResult.conflicts) {\n await resolveInteractive(conflict, resolver);\n }\n }\n\n stateDb.close();\n console.log(\"\\n충돌 해결 완료\");\n } catch (error) {\n console.error(\"오류:\", error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n });\n\nasync function resolveInteractive(conflict: Conflict, resolver: ConflictResolver): Promise<void> {\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 const diff = resolver.generateDiff(conflict);\n const diffLines = diff.split(\"\\n\");\n for (const line of diffLines) {\n if (line.startsWith(\"- \")) {\n console.log(`\\x1b[31m${line}\\x1b[0m`);\n } else if (line.startsWith(\"+ \")) {\n console.log(`\\x1b[32m${line}\\x1b[0m`);\n } else if (line.startsWith(\"---\") || line.startsWith(\"+++\")) {\n console.log(`\\x1b[1m${line}\\x1b[0m`);\n } else {\n console.log(line);\n }\n }\n\n const choice = await select<ResolutionChoice>({\n message: \"어떻게 해결할까요?\",\n choices: [\n { name: \"로컬 유지 (Obsidian 버전 유지)\", value: \"local\" },\n { name: \"원격 유지 (Notion 버전으로 덮어쓰기)\", value: \"remote\" },\n { name: \"자동 병합 (3-way merge)\", value: \"merge\" },\n { name: \"복제 (원본 유지 + .conflict 파일 생성)\", value: \"duplicate\" },\n ],\n });\n\n const result = await resolver.resolve(conflict, choice);\n\n if (result.success) {\n console.log(`\\x1b[32m✓ ${path} → ${choice}로 해결\\x1b[0m`);\n } else if (result.mergeHadConflicts) {\n console.log(`\\x1b[33m⚠ ${path} → 자동 병합 완료 (수동 확인 필요한 충돌 마커 있음)\\x1b[0m`);\n } else {\n console.log(`\\x1b[31m✗ ${path} → 해결 실패\\x1b[0m`);\n }\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n WatchSyncService,\n} from \"@im-nobsidian/core\";\nimport type { SyncResult } 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 push = `Push: +${result.push.created} ~${result.push.updated} -${result.push.deleted}`;\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\nexport const watchCommand = new Command(\"watch\")\n .description(\"파일 변경 감지 + 자동 동기화\")\n .option(\"-d, --debounce <ms>\", \"디바운스 대기 시간(ms)\", \"2000\")\n .option(\"-i, --interval <seconds>\", \"풀 동기화 주기(초, 0이면 비활성)\", \"0\")\n .action(async (options: { debounce: string; interval: 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 const client = new NotionClient({\n token: config.notion.token,\n concurrency: config.advanced.concurrency,\n timeoutMs: config.advanced.timeoutMs,\n });\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const debounceMs = parseInt(options.debounce, 10);\n const intervalSec = parseInt(options.interval, 10);\n\n const service = new WatchSyncService(cwd, orchestrator, {\n debounceMs,\n onFileChange: (event, path) => {\n console.log(`[${timestamp()}] ${event}: ${path}`);\n },\n onSyncStart: () => {\n console.log(`[${timestamp()}] 동기화 시작...`);\n },\n onSyncComplete: (result) => {\n console.log(`[${timestamp()}] 동기화 완료 — ${formatResult(result)}`);\n },\n onSyncError: (error) => {\n console.error(`[${timestamp()}] 동기화 실패: ${error.message}`);\n },\n });\n\n let pullTimer: ReturnType<typeof setInterval> | null = null;\n\n const shutdown = async (): Promise<void> => {\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 pullTimer = setInterval(async () => {\n if (service.isSyncing()) return;\n try {\n console.log(`[${timestamp()}] 주기적 동기화 시작...`);\n const result = await orchestrator.sync();\n console.log(`[${timestamp()}] 주기적 동기화 완료 — ${formatResult(result)}`);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`[${timestamp()}] 주기적 동기화 실패: ${msg}`);\n }\n }, intervalSec * 1000);\n }\n\n await new Promise(() => {});\n });\n"],"mappings":"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","names":["Command","chalk","chalk","spinner","Command","ConfigManager","NotionClient","chalk","Command","ConfigManager","NotionClient","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","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","confirm","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "im-nobsidian",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "CLI tool for Obsidian ↔ Notion bidirectional sync",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,10 +12,11 @@
12
12
  ],
13
13
  "dependencies": {
14
14
  "@inquirer/prompts": "^7.0.0",
15
+ "chalk": "^5.6.2",
15
16
  "commander": "^12.0.0",
16
17
  "diff": "^9.0.0",
17
18
  "ora": "^8.0.0",
18
- "@im-nobsidian/core": "0.1.6"
19
+ "@im-nobsidian/core": "0.1.7"
19
20
  },
20
21
  "devDependencies": {
21
22
  "@types/diff": "^8.0.0",