@anraktech/sync 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +73 -219
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { Command } from "commander";
5
- import { createInterface as createInterface3 } from "readline/promises";
6
- import { stdin as stdin3, stdout as stdout3 } from "process";
5
+ import { createInterface as createInterface2 } from "readline/promises";
6
+ import { stdin as stdin2, stdout as stdout2 } from "process";
7
7
  import { existsSync as existsSync3, statSync as statSync2 } from "fs";
8
8
  import { resolve as resolve2 } from "path";
9
- import chalk4 from "chalk";
9
+ import chalk3 from "chalk";
10
10
 
11
11
  // src/config.ts
12
12
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -418,9 +418,6 @@ function resetCache() {
418
418
  import { watch } from "chokidar";
419
419
  import { readdirSync, statSync, existsSync as existsSync2 } from "fs";
420
420
  import { join as join2, relative as relative2, basename as basename4, resolve } from "path";
421
- import { createInterface as createInterface2 } from "readline/promises";
422
- import { stdin as stdin2, stdout as stdout2 } from "process";
423
- import chalk3 from "chalk";
424
421
 
425
422
  // src/uploader.ts
426
423
  import { stat as stat2 } from "fs/promises";
@@ -628,13 +625,13 @@ var TOOLS = [
628
625
  type: "function",
629
626
  function: {
630
627
  name: "scan_folder",
631
- description: "Scan a local folder on the user's computer and sync all supported files (PDF, DOCX, etc.) to a matching legal case. Use when user asks to scan, sync, upload, or look at a specific folder or directory.",
628
+ description: "Scan a local folder on the user's computer and sync all supported files (PDF, DOCX, etc.) to a matching legal case. Use when user asks to scan, sync, upload, or look at a specific folder.",
632
629
  parameters: {
633
630
  type: "object",
634
631
  properties: {
635
632
  folderPath: {
636
633
  type: "string",
637
- description: "Absolute path to the folder to scan, e.g. /Users/kapil/Downloads"
634
+ description: "Absolute path to the folder, e.g. /Users/name/Downloads"
638
635
  }
639
636
  },
640
637
  required: ["folderPath"]
@@ -645,7 +642,7 @@ var TOOLS = [
645
642
  type: "function",
646
643
  function: {
647
644
  name: "list_cases",
648
- description: "List all legal cases on the AnrakLegal server. Shows case numbers, names, and document counts.",
645
+ description: "List all legal cases on the server with case numbers, names, and document counts.",
649
646
  parameters: { type: "object", properties: {} }
650
647
  }
651
648
  },
@@ -653,7 +650,7 @@ var TOOLS = [
653
650
  type: "function",
654
651
  function: {
655
652
  name: "get_status",
656
- description: "Show sync statistics: files tracked, synced, pending, errors, queue size, and mapped folders.",
653
+ description: "Show sync stats: files tracked, synced, pending, errors, queue size.",
657
654
  parameters: { type: "object", properties: {} }
658
655
  }
659
656
  },
@@ -661,7 +658,7 @@ var TOOLS = [
661
658
  type: "function",
662
659
  function: {
663
660
  name: "get_mappings",
664
- description: "Show all folder-to-case mappings. Shows which local folders are mapped to which legal cases.",
661
+ description: "Show which local folders are mapped to which legal cases.",
665
662
  parameters: { type: "object", properties: {} }
666
663
  }
667
664
  },
@@ -669,7 +666,7 @@ var TOOLS = [
669
666
  type: "function",
670
667
  function: {
671
668
  name: "refresh_cases",
672
- description: "Refresh the case list from the server to pick up any newly created cases.",
669
+ description: "Refresh the case list from the server.",
673
670
  parameters: { type: "object", properties: {} }
674
671
  }
675
672
  },
@@ -677,37 +674,31 @@ var TOOLS = [
677
674
  type: "function",
678
675
  function: {
679
676
  name: "rescan_watch_folder",
680
- description: "Re-scan the configured watch folder for new or changed files and sync them.",
677
+ description: "Re-scan the watch folder for new or changed files and sync them.",
681
678
  parameters: { type: "object", properties: {} }
682
679
  }
683
680
  }
684
681
  ];
685
682
  function buildSystemPrompt(config) {
686
- return `You are AnrakLegal Sync Assistant, running inside a terminal CLI tool.
687
- You help lawyers sync local files to their AnrakLegal case management system.
683
+ return `You are AnrakLegal Sync, a terminal assistant that helps lawyers sync local files to their case management system.
688
684
 
689
- Current watch folder: ${config.watchFolder}
685
+ Watch folder: ${config.watchFolder}
690
686
  Server: ${config.apiUrl}
691
687
 
692
- You can:
693
- - Scan any folder on the user's computer and sync files to cases
694
- - List cases on the server
695
- - Show sync status and folder-to-case mappings
696
- - Re-scan the watch folder for changes
697
- - Refresh the case list
698
-
699
- When the user asks you to do something, call the appropriate tool. After getting tool results, summarize them conversationally and concisely \u2014 this is a terminal, keep it brief.
700
-
701
- If the user mentions a folder path, use scan_folder. If they say something like "my downloads" or "desktop", expand it to a likely absolute path based on common OS conventions (e.g. /Users/<username>/Downloads on macOS).
688
+ You can scan folders, list cases, show sync status, and more using your tools.
702
689
 
703
- Do NOT use markdown formatting. Use plain text only. Keep responses short (1-3 lines unless showing a list).`;
690
+ Rules:
691
+ - Be concise. This is a terminal \u2014 1-3 lines unless showing a list.
692
+ - Do NOT use markdown. Plain text only.
693
+ - When user mentions a folder like "downloads" or "desktop", expand to full path (macOS: /Users/<name>/Downloads, Windows: C:\\Users\\<name>\\Downloads).
694
+ - Call tools to perform actions. Summarize results naturally after.
695
+ - Do NOT use thinking tags or reasoning tags in your output.`;
704
696
  }
705
697
  async function executeTool(name, args, ctx) {
706
698
  switch (name) {
707
699
  case "scan_folder": {
708
700
  const folderPath = args.folderPath;
709
701
  if (!folderPath) return JSON.stringify({ error: "Missing folderPath" });
710
- log.dim(`Scanning ${folderPath}...`);
711
702
  try {
712
703
  await ctx.scanFolder(folderPath);
713
704
  const stats = getStats();
@@ -720,9 +711,7 @@ async function executeTool(name, args, ctx) {
720
711
  errors: stats.errors
721
712
  });
722
713
  } catch (err) {
723
- return JSON.stringify({
724
- error: err instanceof Error ? err.message : String(err)
725
- });
714
+ return JSON.stringify({ error: err instanceof Error ? err.message : String(err) });
726
715
  }
727
716
  }
728
717
  case "list_cases": {
@@ -753,13 +742,9 @@ async function executeTool(name, args, ctx) {
753
742
  }
754
743
  case "refresh_cases": {
755
744
  await ctx.refreshCases();
756
- return JSON.stringify({
757
- success: true,
758
- caseCount: ctx.getCases().length
759
- });
745
+ return JSON.stringify({ success: true, caseCount: ctx.getCases().length });
760
746
  }
761
747
  case "rescan_watch_folder": {
762
- log.dim(`Re-scanning ${ctx.config.watchFolder}...`);
763
748
  await ctx.triggerScan();
764
749
  const stats = getStats();
765
750
  return JSON.stringify({
@@ -792,43 +777,41 @@ async function* parseSSE(response) {
792
777
  const parsed = JSON.parse(data);
793
778
  const choice = parsed.choices?.[0];
794
779
  if (choice?.delta) {
795
- yield {
796
- delta: choice.delta,
797
- finishReason: choice.finish_reason ?? null
798
- };
780
+ yield { delta: choice.delta, finishReason: choice.finish_reason ?? null };
799
781
  }
800
782
  } catch {
801
783
  }
802
784
  }
803
785
  }
804
786
  }
805
- var MODEL = "qwen/qwen3-8b";
806
787
  async function agentTurn(messages, ctx) {
807
- const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
788
+ const token = await getAccessToken(ctx.config);
789
+ const response = await fetch(`${ctx.config.apiUrl}/api/sync/chat`, {
808
790
  method: "POST",
809
791
  headers: {
810
- Authorization: `Bearer ${ctx.apiKey}`,
811
- "Content-Type": "application/json",
812
- "HTTP-Referer": "https://anrak.legal",
813
- "X-Title": "AnrakLegal Sync CLI"
792
+ Authorization: `Bearer ${token}`,
793
+ "Content-Type": "application/json"
814
794
  },
815
- body: JSON.stringify({
816
- model: MODEL,
817
- messages,
818
- tools: TOOLS,
819
- stream: true
820
- })
795
+ body: JSON.stringify({ messages, tools: TOOLS })
821
796
  });
822
797
  if (!response.ok) {
823
798
  const body = await response.text().catch(() => "");
824
- throw new Error(`API error (${response.status}): ${body.slice(0, 200)}`);
799
+ throw new Error(`Server error (${response.status}): ${body.slice(0, 200)}`);
825
800
  }
826
801
  let textContent = "";
827
802
  const toolCalls = /* @__PURE__ */ new Map();
803
+ let isFirstText = true;
828
804
  for await (const { delta } of parseSSE(response)) {
829
805
  if (delta.content) {
830
- process.stdout.write(delta.content);
831
- textContent += delta.content;
806
+ if (isFirstText) {
807
+ process.stdout.write(" ");
808
+ isFirstText = false;
809
+ }
810
+ const cleaned = delta.content.replace(/<\/?think>/g, "");
811
+ if (cleaned) {
812
+ process.stdout.write(cleaned);
813
+ textContent += cleaned;
814
+ }
832
815
  }
833
816
  if (delta.tool_calls) {
834
817
  for (const tc of delta.tool_calls) {
@@ -862,29 +845,33 @@ async function agentTurn(messages, ctx) {
862
845
  };
863
846
  messages.push(assistantMsg);
864
847
  for (const tc of toolCalls.values()) {
848
+ process.stdout.write(chalk2.dim(` \u27F3 ${tc.name}...
849
+ `));
865
850
  let args = {};
866
851
  try {
867
852
  args = JSON.parse(tc.args || "{}");
868
853
  } catch {
869
854
  }
870
855
  const result = await executeTool(tc.name, args, ctx);
871
- messages.push({
872
- role: "tool",
873
- content: result,
874
- tool_call_id: tc.id
875
- });
856
+ messages.push({ role: "tool", content: result, tool_call_id: tc.id });
876
857
  }
877
858
  return agentTurn(messages, ctx);
878
859
  }
879
860
  function startAIAgent(ctx) {
880
- const PROMPT = chalk2.blue("anrak> ");
881
861
  const history = [
882
862
  { role: "system", content: buildSystemPrompt(ctx.config) }
883
863
  ];
884
864
  const rl = createInterface({ input: stdin, output: stdout });
885
865
  console.log("");
886
- log.info("AI assistant ready. Type naturally \u2014 ask me to scan folders, show cases, etc.");
887
- log.dim('Type "quit" to exit.\n');
866
+ console.log(chalk2.bold(" AnrakLegal Sync"));
867
+ console.log(chalk2.dim(` Watching ${ctx.config.watchFolder}`));
868
+ console.log(chalk2.dim(` Connected to ${ctx.config.apiUrl}`));
869
+ console.log("");
870
+ console.log(
871
+ chalk2.dim(" Ask me anything \u2014 scan folders, check cases, show status.")
872
+ );
873
+ console.log(chalk2.dim(' Type "quit" to exit.\n'));
874
+ const PROMPT = `${chalk2.bold.blue("\u276F")} `;
888
875
  async function promptLoop() {
889
876
  while (true) {
890
877
  let input;
@@ -901,7 +888,7 @@ function startAIAgent(ctx) {
901
888
  }
902
889
  if (/^(clear|reset|new)$/i.test(trimmed)) {
903
890
  history.length = 1;
904
- log.success("Conversation cleared");
891
+ console.log(chalk2.dim(" Conversation cleared.\n"));
905
892
  continue;
906
893
  }
907
894
  history.push({ role: "user", content: trimmed });
@@ -915,10 +902,7 @@ function startAIAgent(ctx) {
915
902
  }
916
903
  } catch (err) {
917
904
  const msg = err instanceof Error ? err.message : String(err);
918
- log.error(msg);
919
- if (msg.includes("401") || msg.includes("403")) {
920
- log.warn("Check your OpenRouter API key (OPENROUTER_API_KEY env var)");
921
- }
905
+ console.log(chalk2.red(` Error: ${msg}`));
922
906
  }
923
907
  console.log("");
924
908
  }
@@ -1021,129 +1005,6 @@ async function scanExternalFolder(config, folderPath, cases) {
1021
1005
  log.success("Everything up to date");
1022
1006
  }
1023
1007
  }
1024
- function startInteractiveAgent(config, getCases, refreshCases, triggerScan) {
1025
- const PROMPT = chalk3.blue("anrak> ");
1026
- const rl = createInterface2({ input: stdin2, output: stdout2 });
1027
- function showHelp() {
1028
- console.log("");
1029
- console.log(chalk3.bold(" Available commands:"));
1030
- console.log("");
1031
- console.log(` ${chalk3.cyan("scan <folder>")} Scan a folder and sync its files to a case`);
1032
- console.log(` ${chalk3.cyan("rescan")} Re-scan the watch folder for new files`);
1033
- console.log(` ${chalk3.cyan("cases")} List cases on the server`);
1034
- console.log(` ${chalk3.cyan("status")} Show sync statistics`);
1035
- console.log(` ${chalk3.cyan("mappings")} Show folder \u2192 case mappings`);
1036
- console.log(` ${chalk3.cyan("refresh")} Refresh cases from server`);
1037
- console.log(` ${chalk3.cyan("help")} Show this help`);
1038
- console.log(` ${chalk3.cyan("quit")} Stop syncing and exit`);
1039
- console.log("");
1040
- }
1041
- async function handleCommand(input) {
1042
- const trimmed = input.trim();
1043
- if (!trimmed) return;
1044
- const [cmd, ...args] = trimmed.split(/\s+/);
1045
- const arg = args.join(" ");
1046
- switch (cmd.toLowerCase()) {
1047
- case "scan": {
1048
- if (!arg) {
1049
- log.warn("Usage: scan <folder path>");
1050
- break;
1051
- }
1052
- await scanExternalFolder(config, arg, getCases());
1053
- break;
1054
- }
1055
- case "rescan": {
1056
- await triggerScan();
1057
- break;
1058
- }
1059
- case "cases": {
1060
- await refreshCases();
1061
- const cases = getCases();
1062
- if (cases.length === 0) {
1063
- log.info("No cases found on server");
1064
- } else {
1065
- console.log("");
1066
- for (const c of cases) {
1067
- const docCount = c.documents?.length ?? 0;
1068
- console.log(
1069
- ` ${chalk3.cyan(c.caseNumber)} ${c.caseName} ${chalk3.dim(`(${docCount} docs)`)}`
1070
- );
1071
- }
1072
- console.log("");
1073
- }
1074
- break;
1075
- }
1076
- case "status": {
1077
- const stats = getStats();
1078
- console.log("");
1079
- console.log(` ${chalk3.bold("Watch folder:")} ${config.watchFolder}`);
1080
- console.log(` ${chalk3.bold("Files tracked:")} ${stats.totalFiles}`);
1081
- console.log(` ${chalk3.bold("Synced:")} ${chalk3.green(String(stats.synced))}`);
1082
- console.log(` ${chalk3.bold("Pending:")} ${chalk3.yellow(String(stats.pending))}`);
1083
- console.log(` ${chalk3.bold("Errors:")} ${chalk3.red(String(stats.errors))}`);
1084
- console.log(` ${chalk3.bold("Mapped folders:")} ${stats.mappedFolders}`);
1085
- console.log(` ${chalk3.bold("Queue:")} ${queueSize()}`);
1086
- console.log("");
1087
- break;
1088
- }
1089
- case "mappings":
1090
- case "map": {
1091
- const mappings = getAllMappings();
1092
- const entries = Object.entries(mappings);
1093
- if (entries.length === 0) {
1094
- log.info("No folder mappings yet");
1095
- } else {
1096
- console.log("");
1097
- for (const [folder, m] of entries) {
1098
- console.log(
1099
- ` ${chalk3.cyan(folder)} \u2192 ${m.caseNumber} ${chalk3.dim(`(${m.caseName})`)}`
1100
- );
1101
- }
1102
- console.log("");
1103
- }
1104
- break;
1105
- }
1106
- case "refresh": {
1107
- log.info("Refreshing cases from server...");
1108
- await refreshCases();
1109
- log.success(`Found ${getCases().length} case(s)`);
1110
- break;
1111
- }
1112
- case "help":
1113
- case "?": {
1114
- showHelp();
1115
- break;
1116
- }
1117
- case "quit":
1118
- case "exit":
1119
- case "q": {
1120
- process.emit("SIGINT");
1121
- return;
1122
- }
1123
- default: {
1124
- log.warn(`Unknown command: ${cmd}. Type ${chalk3.cyan("help")} for available commands.`);
1125
- break;
1126
- }
1127
- }
1128
- }
1129
- showHelp();
1130
- async function promptLoop() {
1131
- while (true) {
1132
- let input;
1133
- try {
1134
- input = await rl.question(PROMPT);
1135
- } catch {
1136
- break;
1137
- }
1138
- try {
1139
- await handleCommand(input);
1140
- } catch (err) {
1141
- log.error(err instanceof Error ? err.message : String(err));
1142
- }
1143
- }
1144
- }
1145
- void promptLoop();
1146
- }
1147
1008
  async function startWatching(config) {
1148
1009
  const folder = config.watchFolder;
1149
1010
  log.info(`Scanning ${folder}...`);
@@ -1211,8 +1072,7 @@ async function startWatching(config) {
1211
1072
  watcher.on("error", (err) => {
1212
1073
  log.error(`Watcher error: ${err instanceof Error ? err.message : String(err)}`);
1213
1074
  });
1214
- const apiKey = process.env.OPENROUTER_API_KEY ?? config.openrouterApiKey;
1215
- const agentContext = {
1075
+ startAIAgent({
1216
1076
  config,
1217
1077
  getCases: () => cases,
1218
1078
  refreshCases: async () => {
@@ -1230,13 +1090,7 @@ async function startWatching(config) {
1230
1090
  scanFolder: async (folderPath) => {
1231
1091
  await scanExternalFolder(config, folderPath, cases);
1232
1092
  }
1233
- };
1234
- if (apiKey) {
1235
- startAIAgent({ ...agentContext, apiKey });
1236
- } else {
1237
- log.dim("Tip: Set OPENROUTER_API_KEY for AI-powered natural language mode\n");
1238
- startInteractiveAgent(config, agentContext.getCases, agentContext.refreshCases, agentContext.triggerScan);
1239
- }
1093
+ });
1240
1094
  const shutdown = () => {
1241
1095
  log.info("Shutting down...");
1242
1096
  clearInterval(refreshInterval);
@@ -1260,11 +1114,11 @@ var pkg = JSON.parse(readFileSync2(join3(__dirname2, "..", "package.json"), "utf
1260
1114
  var program = new Command();
1261
1115
  program.name("anrak-sync").description("AnrakLegal desktop file sync \u2014 watches local folders, syncs to case management").version(pkg.version);
1262
1116
  program.command("init").description("Set up AnrakLegal Sync (first-time configuration)").option("--password", "Use email/password login instead of browser").action(async (opts) => {
1263
- const rl = createInterface3({ input: stdin3, output: stdout3 });
1117
+ const rl = createInterface2({ input: stdin2, output: stdout2 });
1264
1118
  try {
1265
- console.log(chalk4.bold.blue("\n AnrakLegal Sync \u2014 Setup\n"));
1119
+ console.log(chalk3.bold.blue("\n AnrakLegal Sync \u2014 Setup\n"));
1266
1120
  const apiUrl = await rl.question(
1267
- ` AnrakLegal URL ${chalk4.dim("(https://anrak.legal)")}: `
1121
+ ` AnrakLegal URL ${chalk3.dim("(https://anrak.legal)")}: `
1268
1122
  ) || "https://anrak.legal";
1269
1123
  log.info("Connecting to server...");
1270
1124
  const serverConfig = await fetchServerConfig(apiUrl);
@@ -1292,10 +1146,10 @@ program.command("init").description("Set up AnrakLegal Sync (first-time configur
1292
1146
  tokens = await browserLogin(apiUrl);
1293
1147
  }
1294
1148
  log.success("Authenticated");
1295
- const rl2 = createInterface3({ input: stdin3, output: stdout3 });
1149
+ const rl2 = createInterface2({ input: stdin2, output: stdout2 });
1296
1150
  const defaultFolder = process.platform === "win32" ? "C:\\Cases" : `${process.env.HOME}/Cases`;
1297
1151
  const watchInput = await rl2.question(
1298
- ` Watch folder ${chalk4.dim(`(${defaultFolder})`)}: `
1152
+ ` Watch folder ${chalk3.dim(`(${defaultFolder})`)}: `
1299
1153
  );
1300
1154
  const watchFolder = resolve2(watchInput || defaultFolder);
1301
1155
  rl2.close();
@@ -1321,7 +1175,7 @@ program.command("init").description("Set up AnrakLegal Sync (first-time configur
1321
1175
  log.info(`Config saved to ${getConfigDir()}`);
1322
1176
  log.info(`Watching: ${watchFolder}`);
1323
1177
  console.log(
1324
- chalk4.dim("\n Run `anrak-sync start` to begin syncing\n")
1178
+ chalk3.dim("\n Run `anrak-sync start` to begin syncing\n")
1325
1179
  );
1326
1180
  } catch (err) {
1327
1181
  log.error(err instanceof Error ? err.message : String(err));
@@ -1333,7 +1187,7 @@ program.command("login").description("Re-authenticate with AnrakLegal").option("
1333
1187
  try {
1334
1188
  let tokens;
1335
1189
  if (opts.password) {
1336
- const rl = createInterface3({ input: stdin3, output: stdout3 });
1190
+ const rl = createInterface2({ input: stdin2, output: stdout2 });
1337
1191
  try {
1338
1192
  const email = await rl.question(" Email: ");
1339
1193
  const password = await rl.question(" Password: ");
@@ -1356,7 +1210,7 @@ program.command("login").description("Re-authenticate with AnrakLegal").option("
1356
1210
  });
1357
1211
  program.command("start").description("Start watching for file changes and syncing").action(async () => {
1358
1212
  const config = requireConfig();
1359
- console.log(chalk4.bold.blue("\n AnrakLegal Sync\n"));
1213
+ console.log(chalk3.bold.blue("\n AnrakLegal Sync\n"));
1360
1214
  log.info(`Watching: ${config.watchFolder}`);
1361
1215
  log.info(`Server: ${config.apiUrl}`);
1362
1216
  console.log("");
@@ -1369,7 +1223,7 @@ program.command("start").description("Start watching for file changes and syncin
1369
1223
  });
1370
1224
  program.command("push").description("One-time sync \u2014 upload all new/changed files, then exit").action(async () => {
1371
1225
  const config = requireConfig();
1372
- console.log(chalk4.bold.blue("\n AnrakLegal Sync \u2014 Push\n"));
1226
+ console.log(chalk3.bold.blue("\n AnrakLegal Sync \u2014 Push\n"));
1373
1227
  log.info(`Folder: ${config.watchFolder}`);
1374
1228
  log.info(`Server: ${config.apiUrl}`);
1375
1229
  console.log("");
@@ -1383,24 +1237,24 @@ program.command("push").description("One-time sync \u2014 upload all new/changed
1383
1237
  program.command("status").description("Show sync status").action(async () => {
1384
1238
  const config = requireConfig();
1385
1239
  const stats = getStats();
1386
- console.log(chalk4.bold.blue("\n AnrakLegal Sync \u2014 Status\n"));
1240
+ console.log(chalk3.bold.blue("\n AnrakLegal Sync \u2014 Status\n"));
1387
1241
  console.log(` Server: ${config.apiUrl}`);
1388
1242
  console.log(` Watch folder: ${config.watchFolder}`);
1389
1243
  console.log(` Config: ${getConfigDir()}`);
1390
1244
  console.log("");
1391
1245
  console.log(` Files tracked: ${stats.totalFiles}`);
1392
- console.log(` Synced: ${chalk4.green(stats.synced)}`);
1393
- console.log(` Pending: ${chalk4.yellow(stats.pending)}`);
1394
- console.log(` Errors: ${chalk4.red(stats.errors)}`);
1246
+ console.log(` Synced: ${chalk3.green(stats.synced)}`);
1247
+ console.log(` Pending: ${chalk3.yellow(stats.pending)}`);
1248
+ console.log(` Errors: ${chalk3.red(stats.errors)}`);
1395
1249
  console.log(` Mapped folders: ${stats.mappedFolders}`);
1396
1250
  try {
1397
1251
  const cases = await listCases(config);
1398
1252
  console.log(`
1399
1253
  Server cases: ${cases.length}`);
1400
- console.log(` Auth: ${chalk4.green("valid")}`);
1254
+ console.log(` Auth: ${chalk3.green("valid")}`);
1401
1255
  } catch {
1402
1256
  console.log(`
1403
- Auth: ${chalk4.red("expired \u2014 run anrak-sync login")}`);
1257
+ Auth: ${chalk3.red("expired \u2014 run anrak-sync login")}`);
1404
1258
  }
1405
1259
  console.log("");
1406
1260
  });
@@ -1408,13 +1262,13 @@ program.command("map").description("Show folder-to-case mappings").action(async
1408
1262
  const config = requireConfig();
1409
1263
  const mappings = getAllMappings();
1410
1264
  const entries = Object.entries(mappings);
1411
- console.log(chalk4.bold.blue("\n AnrakLegal Sync \u2014 Mappings\n"));
1265
+ console.log(chalk3.bold.blue("\n AnrakLegal Sync \u2014 Mappings\n"));
1412
1266
  if (entries.length === 0) {
1413
1267
  log.info("No mappings yet. Run `anrak-sync push` or `anrak-sync start` to create them.");
1414
1268
  } else {
1415
1269
  for (const [folder, mapping] of entries) {
1416
1270
  console.log(
1417
- ` ${chalk4.cyan(folder)} -> ${mapping.caseNumber} (${chalk4.dim(mapping.caseName)})`
1271
+ ` ${chalk3.cyan(folder)} -> ${mapping.caseNumber} (${chalk3.dim(mapping.caseName)})`
1418
1272
  );
1419
1273
  }
1420
1274
  }
@@ -1423,9 +1277,9 @@ program.command("map").description("Show folder-to-case mappings").action(async
1423
1277
  const mappedIds = new Set(entries.map(([, m]) => m.caseId));
1424
1278
  const unmapped = cases.filter((c) => !mappedIds.has(c.id));
1425
1279
  if (unmapped.length > 0) {
1426
- console.log(chalk4.dim("\n Unmapped server cases:"));
1280
+ console.log(chalk3.dim("\n Unmapped server cases:"));
1427
1281
  for (const c of unmapped) {
1428
- console.log(` ${chalk4.dim(c.caseNumber)} ${chalk4.dim(c.caseName)}`);
1282
+ console.log(` ${chalk3.dim(c.caseNumber)} ${chalk3.dim(c.caseName)}`);
1429
1283
  }
1430
1284
  }
1431
1285
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anraktech/sync",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "AnrakLegal desktop file sync agent — watches local folders and syncs to case management",
5
5
  "type": "module",
6
6
  "bin": {