@ncukondo/reference-manager 0.24.0 → 0.25.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 (76) hide show
  1. package/README.md +44 -1
  2. package/dist/chunks/{SearchableMultiSelect-BigM9xv_.js → SearchableMultiSelect-B7qEWPDT.js} +2 -2
  3. package/dist/chunks/{SearchableMultiSelect-BigM9xv_.js.map → SearchableMultiSelect-B7qEWPDT.js.map} +1 -1
  4. package/dist/chunks/{action-menu-ByDpq-KF.js → action-menu-DD0RtNVD.js} +3 -3
  5. package/dist/chunks/{action-menu-ByDpq-KF.js.map → action-menu-DD0RtNVD.js.map} +1 -1
  6. package/dist/chunks/checker-7pzK2XSC.js +92 -0
  7. package/dist/chunks/checker-7pzK2XSC.js.map +1 -0
  8. package/dist/chunks/crossref-client-DGNz4PNW.js +52 -0
  9. package/dist/chunks/crossref-client-DGNz4PNW.js.map +1 -0
  10. package/dist/chunks/fix-interaction-BpfMLRNY.js +203 -0
  11. package/dist/chunks/fix-interaction-BpfMLRNY.js.map +1 -0
  12. package/dist/chunks/{index-CKY11DzK.js → index-CYEise6v.js} +4 -4
  13. package/dist/chunks/{index-CKY11DzK.js.map → index-CYEise6v.js.map} +1 -1
  14. package/dist/chunks/{index-DWAtvFtp.js → index-D2HsxXnK.js} +203 -21
  15. package/dist/chunks/index-D2HsxXnK.js.map +1 -0
  16. package/dist/chunks/{index-DVsuPpMS.js → index-PQkbePWV.js} +3 -3
  17. package/dist/chunks/index-PQkbePWV.js.map +1 -0
  18. package/dist/chunks/{index-ChvsE9WF.js → index-QTYx5RaF.js} +315 -28
  19. package/dist/chunks/index-QTYx5RaF.js.map +1 -0
  20. package/dist/chunks/{loader-CV71qNY2.js → loader-B-fte1uv.js} +19 -8
  21. package/dist/chunks/loader-B-fte1uv.js.map +1 -0
  22. package/dist/chunks/pubmed-client-J18fg3fG.js +51 -0
  23. package/dist/chunks/pubmed-client-J18fg3fG.js.map +1 -0
  24. package/dist/chunks/{reference-select-CrOVXP7v.js → reference-select-Qpgt9cbN.js} +3 -3
  25. package/dist/chunks/{reference-select-CrOVXP7v.js.map → reference-select-Qpgt9cbN.js.map} +1 -1
  26. package/dist/chunks/{style-select-BVP0KQz4.js → style-select-mEMoWbM2.js} +3 -3
  27. package/dist/chunks/{style-select-BVP0KQz4.js.map → style-select-mEMoWbM2.js.map} +1 -1
  28. package/dist/cli/commands/check.d.ts +42 -0
  29. package/dist/cli/commands/check.d.ts.map +1 -0
  30. package/dist/cli/commands/index.d.ts +2 -0
  31. package/dist/cli/commands/index.d.ts.map +1 -1
  32. package/dist/cli/help/search-help.d.ts +6 -0
  33. package/dist/cli/help/search-help.d.ts.map +1 -0
  34. package/dist/cli/index.d.ts.map +1 -1
  35. package/dist/cli/server-client.d.ts +2 -1
  36. package/dist/cli/server-client.d.ts.map +1 -1
  37. package/dist/cli.js +1 -1
  38. package/dist/config/defaults.d.ts.map +1 -1
  39. package/dist/config/env-override.d.ts.map +1 -1
  40. package/dist/config/key-parser.d.ts.map +1 -1
  41. package/dist/config/loader.d.ts.map +1 -1
  42. package/dist/config/schema.d.ts +3 -0
  43. package/dist/config/schema.d.ts.map +1 -1
  44. package/dist/features/check/checker.d.ts +18 -0
  45. package/dist/features/check/checker.d.ts.map +1 -0
  46. package/dist/features/check/crossref-client.d.ts +24 -0
  47. package/dist/features/check/crossref-client.d.ts.map +1 -0
  48. package/dist/features/check/fix-actions.d.ts +16 -0
  49. package/dist/features/check/fix-actions.d.ts.map +1 -0
  50. package/dist/features/check/fix-interaction.d.ts +11 -0
  51. package/dist/features/check/fix-interaction.d.ts.map +1 -0
  52. package/dist/features/check/pubmed-client.d.ts +20 -0
  53. package/dist/features/check/pubmed-client.d.ts.map +1 -0
  54. package/dist/features/check/types.d.ts +23 -0
  55. package/dist/features/check/types.d.ts.map +1 -0
  56. package/dist/features/operations/check.d.ts +28 -0
  57. package/dist/features/operations/check.d.ts.map +1 -0
  58. package/dist/features/operations/index.d.ts +1 -0
  59. package/dist/features/operations/index.d.ts.map +1 -1
  60. package/dist/features/operations/library-operations.d.ts +8 -0
  61. package/dist/features/operations/library-operations.d.ts.map +1 -1
  62. package/dist/features/operations/operations-library.d.ts +2 -0
  63. package/dist/features/operations/operations-library.d.ts.map +1 -1
  64. package/dist/index.js +1 -1
  65. package/dist/mcp/tools/check.d.ts +10 -0
  66. package/dist/mcp/tools/check.d.ts.map +1 -0
  67. package/dist/mcp/tools/index.d.ts.map +1 -1
  68. package/dist/server/index.d.ts.map +1 -1
  69. package/dist/server/routes/check.d.ts +4 -0
  70. package/dist/server/routes/check.d.ts.map +1 -0
  71. package/dist/server.js +3 -3
  72. package/package.json +1 -1
  73. package/dist/chunks/index-ChvsE9WF.js.map +0 -1
  74. package/dist/chunks/index-DVsuPpMS.js.map +0 -1
  75. package/dist/chunks/index-DWAtvFtp.js.map +0 -1
  76. package/dist/chunks/loader-CV71qNY2.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { a } from "./index-DWAtvFtp.js";
2
- import { d, g, l, o, s } from "./index-ChvsE9WF.js";
1
+ import { a } from "./index-D2HsxXnK.js";
2
+ import { d, g, l, o, s } from "./index-QTYx5RaF.js";
3
3
  export {
4
4
  a as addAttachment,
5
5
  d as detachAttachment,
@@ -8,4 +8,4 @@ export {
8
8
  o as openAttachment,
9
9
  s as syncAttachments
10
10
  };
11
- //# sourceMappingURL=index-DVsuPpMS.js.map
11
+ //# sourceMappingURL=index-PQkbePWV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-PQkbePWV.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
@@ -5,9 +5,9 @@ import { promises, readFileSync, existsSync, mkdirSync, writeFileSync } from "no
5
5
  import * as os from "node:os";
6
6
  import * as path from "node:path";
7
7
  import path__default, { join, basename, dirname } from "node:path";
8
- import { n as normalizePathForOutput, d as deleteDirectoryIfEmpty, p as parseFilename, i as isReservedRole, e as ensureDirectory, a as addAttachment, R as RESERVED_ROLES, g as generateFilename, b as findFulltextFiles, c as findFulltextFile, h as extensionToFormat, j as fulltextAttach, k as fulltextGet, l as fulltextDiscover, m as fulltextFetch, o as fulltextConvert, q as getExtension, r as getDefaultExportFromCjs, B as BUILTIN_STYLES, s as getFulltextAttachmentTypes, t as startServerWithFileWatcher } from "./index-DWAtvFtp.js";
8
+ import { n as normalizePathForOutput, d as deleteDirectoryIfEmpty, p as parseFilename, i as isReservedRole, e as ensureDirectory, a as addAttachment, R as RESERVED_ROLES, b as generateFilename, c as findFulltextFiles, h as findFulltextFile, j as extensionToFormat, k as fulltextAttach, l as fulltextGet, m as fulltextDiscover, o as fulltextFetch, q as fulltextConvert, r as getExtension, s as getDefaultExportFromCjs, B as BUILTIN_STYLES, t as getFulltextAttachmentTypes, u as startServerWithFileWatcher } from "./index-D2HsxXnK.js";
9
9
  import { readFile, unlink, stat, readdir, rename } from "node:fs/promises";
10
- import { o as openWithSystemApp, l as loadConfig, e as getDefaultCurrentDirConfigFilename, h as getDefaultUserConfigPath } from "./loader-CV71qNY2.js";
10
+ import { o as openWithSystemApp, l as loadConfig, e as getDefaultCurrentDirConfigFilename, h as getDefaultUserConfigPath } from "./loader-B-fte1uv.js";
11
11
  import { spawn, spawnSync } from "node:child_process";
12
12
  import process$1, { stdin, stdout } from "node:process";
13
13
  import * as readline from "node:readline";
@@ -19,7 +19,7 @@ import "@citation-js/plugin-csl";
19
19
  import { ZodOptional as ZodOptional$2, z } from "zod";
20
20
  import { serve } from "@hono/node-server";
21
21
  const name = "@ncukondo/reference-manager";
22
- const version$1 = "0.24.0";
22
+ const version$1 = "0.25.0";
23
23
  const description$1 = "A local reference management tool using CSL-JSON as the single source of truth";
24
24
  const packageJson = {
25
25
  name,
@@ -902,15 +902,15 @@ class OperationsLibrary {
902
902
  }
903
903
  // High-level operations
904
904
  async search(options) {
905
- const { searchReferences } = await import("./index-DWAtvFtp.js").then((n) => n.A);
905
+ const { searchReferences } = await import("./index-D2HsxXnK.js").then((n) => n.E);
906
906
  return searchReferences(this.library, options);
907
907
  }
908
908
  async list(options) {
909
- const { listReferences } = await import("./index-DWAtvFtp.js").then((n) => n.z);
909
+ const { listReferences } = await import("./index-D2HsxXnK.js").then((n) => n.D);
910
910
  return listReferences(this.library, options ?? {});
911
911
  }
912
912
  async cite(options) {
913
- const { citeReferences } = await import("./index-DWAtvFtp.js").then((n) => n.y);
913
+ const { citeReferences } = await import("./index-D2HsxXnK.js").then((n) => n.C);
914
914
  const defaultStyle = options.defaultStyle ?? this.citationConfig?.defaultStyle;
915
915
  const cslDirectory = options.cslDirectory ?? this.citationConfig?.cslDirectory;
916
916
  const mergedOptions = {
@@ -921,32 +921,36 @@ class OperationsLibrary {
921
921
  return citeReferences(this.library, mergedOptions);
922
922
  }
923
923
  async import(inputs, options) {
924
- const { addReferences } = await import("./index-DWAtvFtp.js").then((n) => n.x);
924
+ const { addReferences } = await import("./index-D2HsxXnK.js").then((n) => n.z);
925
925
  return addReferences(inputs, this.library, options ?? {});
926
926
  }
927
+ async check(options) {
928
+ const { checkReferences } = await import("./index-D2HsxXnK.js").then((n) => n.A);
929
+ return checkReferences(this.library, options);
930
+ }
927
931
  // Attachment operations
928
932
  async attachAdd(options) {
929
- const { addAttachment: addAttachment2 } = await import("./index-DVsuPpMS.js");
933
+ const { addAttachment: addAttachment2 } = await import("./index-PQkbePWV.js");
930
934
  return addAttachment2(this.library, options);
931
935
  }
932
936
  async attachList(options) {
933
- const { listAttachments: listAttachments2 } = await import("./index-DVsuPpMS.js");
937
+ const { listAttachments: listAttachments2 } = await import("./index-PQkbePWV.js");
934
938
  return listAttachments2(this.library, options);
935
939
  }
936
940
  async attachGet(options) {
937
- const { getAttachment: getAttachment2 } = await import("./index-DVsuPpMS.js");
941
+ const { getAttachment: getAttachment2 } = await import("./index-PQkbePWV.js");
938
942
  return getAttachment2(this.library, options);
939
943
  }
940
944
  async attachDetach(options) {
941
- const { detachAttachment: detachAttachment2 } = await import("./index-DVsuPpMS.js");
945
+ const { detachAttachment: detachAttachment2 } = await import("./index-PQkbePWV.js");
942
946
  return detachAttachment2(this.library, options);
943
947
  }
944
948
  async attachSync(options) {
945
- const { syncAttachments: syncAttachments2 } = await import("./index-DVsuPpMS.js");
949
+ const { syncAttachments: syncAttachments2 } = await import("./index-PQkbePWV.js");
946
950
  return syncAttachments2(this.library, options);
947
951
  }
948
952
  async attachOpen(options) {
949
- const { openAttachment: openAttachment2 } = await import("./index-DVsuPpMS.js");
953
+ const { openAttachment: openAttachment2 } = await import("./index-PQkbePWV.js");
950
954
  return openAttachment2(this.library, options);
951
955
  }
952
956
  }
@@ -1084,6 +1088,18 @@ class ServerClient {
1084
1088
  }
1085
1089
  return await response.json();
1086
1090
  }
1091
+ async check(options) {
1092
+ const url2 = `${this.baseUrl}/api/check`;
1093
+ const response = await fetch(url2, {
1094
+ method: "POST",
1095
+ headers: { "Content-Type": "application/json" },
1096
+ body: JSON.stringify(options)
1097
+ });
1098
+ if (!response.ok) {
1099
+ throw new Error(await response.text());
1100
+ }
1101
+ return await response.json();
1102
+ }
1087
1103
  /**
1088
1104
  * Generate citations for references.
1089
1105
  * @param options - Cite options including identifiers and formatting
@@ -1790,7 +1806,7 @@ function getAttachExitCode(result) {
1790
1806
  }
1791
1807
  async function executeInteractiveSelect$2(context, config2) {
1792
1808
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
1793
- const { selectReferencesOrExit } = await import("./reference-select-CrOVXP7v.js");
1809
+ const { selectReferencesOrExit } = await import("./reference-select-Qpgt9cbN.js");
1794
1810
  const allReferences = await context.library.getAll();
1795
1811
  const identifiers = await withAlternateScreen2(
1796
1812
  () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -2281,6 +2297,192 @@ const attach = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
2281
2297
  runInteractiveMode,
2282
2298
  syncNewFilesWithRolePrompt
2283
2299
  }, Symbol.toStringTag, { value: "Module" }));
2300
+ function buildCheckOptions(options, appConfig) {
2301
+ const opOptions = {};
2302
+ if (options.all) {
2303
+ opOptions.all = true;
2304
+ } else if (options.search) {
2305
+ opOptions.searchQuery = options.search;
2306
+ } else if (options.identifiers.length > 0) {
2307
+ opOptions.identifiers = options.identifiers;
2308
+ }
2309
+ if (options.uuid) {
2310
+ opOptions.idType = "uuid";
2311
+ }
2312
+ if (options.days !== void 0) {
2313
+ opOptions.skipDays = options.days;
2314
+ }
2315
+ if (options.noSave) {
2316
+ opOptions.save = false;
2317
+ }
2318
+ if (appConfig) {
2319
+ const pubmed = {};
2320
+ if (appConfig.pubmed.email) pubmed.email = appConfig.pubmed.email;
2321
+ if (appConfig.pubmed.apiKey) pubmed.apiKey = appConfig.pubmed.apiKey;
2322
+ opOptions.config = {
2323
+ ...appConfig.email ? { email: appConfig.email } : {},
2324
+ pubmed
2325
+ };
2326
+ }
2327
+ return opOptions;
2328
+ }
2329
+ async function executeCheck(options, context, appConfig) {
2330
+ return context.library.check(buildCheckOptions(options, appConfig));
2331
+ }
2332
+ function getStatusLabel(result) {
2333
+ if (result.status === "skipped") return "[SKIPPED]";
2334
+ if (result.status === "ok") return "[OK]";
2335
+ const finding = result.findings[0];
2336
+ if (!finding) return "[WARNING]";
2337
+ switch (finding.type) {
2338
+ case "retracted":
2339
+ return "[RETRACTED]";
2340
+ case "concern":
2341
+ return "[CONCERN]";
2342
+ case "version_changed":
2343
+ return "[VERSION]";
2344
+ case "metadata_changed":
2345
+ return "[METADATA]";
2346
+ default:
2347
+ return "[WARNING]";
2348
+ }
2349
+ }
2350
+ function formatFindingDetails(finding) {
2351
+ const lines = [];
2352
+ lines.push(` ${finding.message}`);
2353
+ if (finding.details?.retractionDoi) {
2354
+ lines.push(` Retraction notice: https://doi.org/${finding.details.retractionDoi}`);
2355
+ }
2356
+ if (finding.details?.newDoi) {
2357
+ lines.push(` New DOI: https://doi.org/${finding.details.newDoi}`);
2358
+ }
2359
+ return lines;
2360
+ }
2361
+ function formatCheckTextOutput(result) {
2362
+ const lines = [];
2363
+ for (const r of result.results) {
2364
+ const label = getStatusLabel(r);
2365
+ lines.push(`${label} ${r.id}`);
2366
+ for (const finding of r.findings) {
2367
+ lines.push(...formatFindingDetails(finding));
2368
+ }
2369
+ lines.push("");
2370
+ }
2371
+ const { summary } = result;
2372
+ const parts = [`${summary.total} checked`];
2373
+ if (summary.warnings > 0) parts.push(`${summary.warnings} warning(s)`);
2374
+ if (summary.ok > 0) parts.push(`${summary.ok} ok`);
2375
+ if (summary.skipped > 0) parts.push(`${summary.skipped} skipped`);
2376
+ lines.push(`Summary: ${parts.join(", ")}`);
2377
+ return lines.join("\n");
2378
+ }
2379
+ function formatCheckJsonOutput(result, options) {
2380
+ if (!options?.full || !options.items) {
2381
+ return result;
2382
+ }
2383
+ const results = result.results.map((r) => {
2384
+ const item = options.items?.get(r.id);
2385
+ return item ? { ...r, item } : r;
2386
+ });
2387
+ return { ...result, results };
2388
+ }
2389
+ async function handleCheckAction(identifiers, options, globalOpts) {
2390
+ const outputFormat = options.output ?? "text";
2391
+ if (options.fix && !isTTY()) {
2392
+ outputCheckError(new Error("--fix requires an interactive terminal (TTY)"), outputFormat);
2393
+ setExitCode(ExitCode.ERROR);
2394
+ return;
2395
+ }
2396
+ try {
2397
+ const config2 = await loadConfigWithOverrides({ ...globalOpts, ...options });
2398
+ const context = await createExecutionContext(config2, Library.load);
2399
+ const ids = await resolveIdentifiers$1(identifiers, options, context, config2);
2400
+ if (ids === null) return;
2401
+ const result = await executeCheck({ ...options, identifiers: ids }, context, config2);
2402
+ const needAllRefs = options.full && outputFormat === "json" || options.fix;
2403
+ const allRefs = needAllRefs ? await context.library.getAll() : void 0;
2404
+ const jsonOptions = buildJsonOptionsFromRefs(options, outputFormat, result, allRefs);
2405
+ outputCheckResult(result, outputFormat, jsonOptions);
2406
+ if (options.fix && result.summary.warnings > 0 && allRefs) {
2407
+ const { runFixInteraction } = await import("./fix-interaction-BpfMLRNY.js");
2408
+ const findItem = (id2) => allRefs.find((item) => item.id === id2);
2409
+ const fixResult = await runFixInteraction(result.results, context.library, findItem);
2410
+ const removedSuffix = fixResult.removed.length > 0 ? `, ${fixResult.removed.length} removed` : "";
2411
+ process.stderr.write(
2412
+ `
2413
+ Fix summary: ${fixResult.applied} applied, ${fixResult.skipped} skipped${removedSuffix}
2414
+ `
2415
+ );
2416
+ }
2417
+ setExitCode(ExitCode.SUCCESS);
2418
+ } catch (error) {
2419
+ outputCheckError(error, outputFormat);
2420
+ setExitCode(ExitCode.ERROR);
2421
+ }
2422
+ }
2423
+ function buildJsonOptionsFromRefs(options, outputFormat, result, allRefs) {
2424
+ if (!options.full || outputFormat !== "json" || !allRefs) return void 0;
2425
+ const items2 = /* @__PURE__ */ new Map();
2426
+ for (const r of result.results) {
2427
+ const item = allRefs.find((ref2) => ref2.id === r.id);
2428
+ if (item) items2.set(r.id, item);
2429
+ }
2430
+ return { full: true, items: items2 };
2431
+ }
2432
+ async function resolveIdentifiers$1(identifiers, options, context, config2) {
2433
+ if (identifiers.length > 0 || options.all || options.search) {
2434
+ return identifiers;
2435
+ }
2436
+ if (isTTY()) {
2437
+ const ids = await selectReferencesInteractively(context, config2);
2438
+ if (ids.length === 0) {
2439
+ setExitCode(ExitCode.SUCCESS);
2440
+ return null;
2441
+ }
2442
+ return ids;
2443
+ }
2444
+ const stdinIds = await readIdentifiersFromStdin();
2445
+ if (stdinIds.length === 0) {
2446
+ process.stderr.write(
2447
+ "Error: No identifiers provided. Provide IDs, use --all, --search, or run interactively.\n"
2448
+ );
2449
+ setExitCode(ExitCode.ERROR);
2450
+ return null;
2451
+ }
2452
+ return stdinIds;
2453
+ }
2454
+ function outputCheckResult(result, format2, jsonOptions) {
2455
+ if (format2 === "json") {
2456
+ process.stdout.write(`${JSON.stringify(formatCheckJsonOutput(result, jsonOptions))}
2457
+ `);
2458
+ } else {
2459
+ process.stderr.write(`${formatCheckTextOutput(result)}
2460
+ `);
2461
+ }
2462
+ }
2463
+ function outputCheckError(error, format2) {
2464
+ const message = error instanceof Error ? error.message : String(error);
2465
+ if (format2 === "json") {
2466
+ process.stdout.write(`${JSON.stringify({ error: message })}
2467
+ `);
2468
+ } else {
2469
+ process.stderr.write(`Error: ${message}
2470
+ `);
2471
+ }
2472
+ }
2473
+ async function selectReferencesInteractively(context, config2) {
2474
+ const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
2475
+ const { selectReferenceItemsOrExit } = await import("./reference-select-Qpgt9cbN.js");
2476
+ const allReferences = await context.library.getAll();
2477
+ if (allReferences.length === 0) {
2478
+ process.stderr.write("No references in library.\n");
2479
+ return [];
2480
+ }
2481
+ const selectedItems = await withAlternateScreen2(
2482
+ () => selectReferenceItemsOrExit(allReferences, { multiSelect: true }, config2.cli.tui)
2483
+ );
2484
+ return selectedItems.map((item) => item.id);
2485
+ }
2284
2486
  async function validateOptions$2(options) {
2285
2487
  if (options.output && !["text", "html", "rtf"].includes(options.output)) {
2286
2488
  throw new Error(`Invalid output format '${options.output}'. Must be one of: text, html, rtf`);
@@ -2338,8 +2540,8 @@ function getCiteExitCode(result) {
2338
2540
  }
2339
2541
  async function executeInteractiveCite(options, context, config2) {
2340
2542
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
2341
- const { runCiteFlow } = await import("./index-CKY11DzK.js");
2342
- const { buildStyleChoices, listCustomStyles } = await import("./style-select-BVP0KQz4.js");
2543
+ const { runCiteFlow } = await import("./index-CYEise6v.js");
2544
+ const { buildStyleChoices, listCustomStyles } = await import("./style-select-mEMoWbM2.js");
2343
2545
  const { search } = await import("./file-watcher-Dlx0PolG.js").then((n) => n.B);
2344
2546
  const { tokenize } = await import("./file-watcher-Dlx0PolG.js").then((n) => n.A);
2345
2547
  const { checkTTY } = await import("./tty-BMyaEOhX.js");
@@ -2419,6 +2621,7 @@ async function handleCiteAction(identifiers, options, globalOpts) {
2419
2621
  }
2420
2622
  const ENV_OVERRIDE_MAP = {
2421
2623
  REFERENCE_MANAGER_LIBRARY: "library",
2624
+ EMAIL: "email",
2422
2625
  REFERENCE_MANAGER_ATTACHMENTS_DIR: "attachments.directory",
2423
2626
  REFERENCE_MANAGER_CLI_DEFAULT_LIMIT: "cli.default_limit",
2424
2627
  REFERENCE_MANAGER_MCP_DEFAULT_LIMIT: "mcp.default_limit",
@@ -2454,6 +2657,7 @@ const CONFIG_KEY_REGISTRY = [
2454
2657
  description: "Log level",
2455
2658
  enumValues: ["silent", "info", "debug"]
2456
2659
  },
2660
+ { key: "email", type: "string", description: "Fallback email for API services", optional: true },
2457
2661
  // backup section
2458
2662
  { key: "backup.max_generations", type: "integer", description: "Maximum backup generations" },
2459
2663
  { key: "backup.max_age_days", type: "integer", description: "Maximum backup age in days" },
@@ -6938,7 +7142,7 @@ function formatEditOutput(result) {
6938
7142
  }
6939
7143
  async function executeInteractiveEdit(options, context, config2) {
6940
7144
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
6941
- const { selectReferencesOrExit } = await import("./reference-select-CrOVXP7v.js");
7145
+ const { selectReferencesOrExit } = await import("./reference-select-Qpgt9cbN.js");
6942
7146
  const allReferences = await context.library.getAll();
6943
7147
  const identifiers = await withAlternateScreen2(
6944
7148
  () => selectReferencesOrExit(allReferences, { multiSelect: true }, config2.cli.tui)
@@ -10479,7 +10683,7 @@ function getFulltextExitCode(result) {
10479
10683
  }
10480
10684
  async function executeInteractiveSelect$1(context, config2) {
10481
10685
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
10482
- const { selectReferencesOrExit } = await import("./reference-select-CrOVXP7v.js");
10686
+ const { selectReferencesOrExit } = await import("./reference-select-Qpgt9cbN.js");
10483
10687
  const allReferences = await context.library.getAll();
10484
10688
  const identifiers = await withAlternateScreen2(
10485
10689
  () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -31328,6 +31532,53 @@ function registerAddTool(server, getLibraryOperations) {
31328
31532
  }
31329
31533
  );
31330
31534
  }
31535
+ function formatCheckResult(result) {
31536
+ const lines = [];
31537
+ for (const r of result.results) {
31538
+ if (r.status === "ok") {
31539
+ lines.push(`[OK] ${r.id}`);
31540
+ } else if (r.status === "skipped") {
31541
+ lines.push(`[SKIPPED] ${r.id}`);
31542
+ } else {
31543
+ for (const f of r.findings) {
31544
+ lines.push(`[${f.type.toUpperCase()}] ${r.id}: ${f.message}`);
31545
+ }
31546
+ }
31547
+ }
31548
+ const { summary } = result;
31549
+ lines.push(
31550
+ `
31551
+ Summary: ${summary.total} checked, ${summary.ok} ok, ${summary.warnings} warnings, ${summary.skipped} skipped`
31552
+ );
31553
+ return lines.join("\n");
31554
+ }
31555
+ function registerCheckTool(server, getLibraryOperations) {
31556
+ server.registerTool(
31557
+ "check",
31558
+ {
31559
+ description: "Check references for retractions, expressions of concern, and version changes by querying Crossref and PubMed APIs.",
31560
+ inputSchema: {
31561
+ ids: z.array(z.string()).optional().describe("Array of reference IDs to check. Omit if using 'all'."),
31562
+ all: z.boolean().optional().describe("Check all references in library"),
31563
+ skipDays: z.number().optional().describe("Skip references checked within n days (default: 7)"),
31564
+ save: z.boolean().optional().describe("Whether to save results to library (default: true)")
31565
+ }
31566
+ },
31567
+ async (args) => {
31568
+ const libraryOps = getLibraryOperations();
31569
+ const options = {};
31570
+ if (args.ids?.length) options.identifiers = args.ids;
31571
+ if (args.all) options.all = true;
31572
+ if (args.skipDays !== void 0) options.skipDays = args.skipDays;
31573
+ if (args.save !== void 0) options.save = args.save;
31574
+ const result = await libraryOps.check(options);
31575
+ const text = formatCheckResult(result);
31576
+ return {
31577
+ content: [{ type: "text", text }]
31578
+ };
31579
+ }
31580
+ );
31581
+ }
31331
31582
  function registerCiteTool(server, getLibraryOperations) {
31332
31583
  server.registerTool(
31333
31584
  "cite",
@@ -31718,6 +31969,7 @@ function registerAllTools(server, getLibraryOperations, getConfig) {
31718
31969
  registerSearchTool(server, getLibraryOperations, getConfig);
31719
31970
  registerListTool(server, getLibraryOperations, getConfig);
31720
31971
  registerCiteTool(server, getLibraryOperations);
31972
+ registerCheckTool(server, getLibraryOperations);
31721
31973
  registerAddTool(server, getLibraryOperations);
31722
31974
  registerRemoveTool(server, getLibraryOperations);
31723
31975
  registerFulltextAttachTool(server, getLibraryOperations, getConfig);
@@ -31779,7 +32031,7 @@ async function mcpStart(options) {
31779
32031
  async function executeRemove(options, context) {
31780
32032
  const { identifier, idType = "id", fulltextDirectory, deleteFulltext = false } = options;
31781
32033
  if (context.mode === "local" && deleteFulltext && fulltextDirectory) {
31782
- const { removeReference } = await import("./index-DWAtvFtp.js").then((n) => n.w);
32034
+ const { removeReference } = await import("./index-D2HsxXnK.js").then((n) => n.x);
31783
32035
  return removeReference(context.library, {
31784
32036
  identifier,
31785
32037
  idType,
@@ -31834,7 +32086,7 @@ Continue?`;
31834
32086
  }
31835
32087
  async function executeInteractiveRemove(context, config2) {
31836
32088
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
31837
- const { selectReferenceItemsOrExit } = await import("./reference-select-CrOVXP7v.js");
32089
+ const { selectReferenceItemsOrExit } = await import("./reference-select-Qpgt9cbN.js");
31838
32090
  const allReferences = await context.library.getAll();
31839
32091
  const selectedItems = await withAlternateScreen2(
31840
32092
  () => selectReferenceItemsOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -32059,7 +32311,7 @@ async function executeInteractiveSearch(options, context, config2) {
32059
32311
  validateInteractiveOptions(options);
32060
32312
  const { checkTTY } = await import("./tty-BMyaEOhX.js");
32061
32313
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
32062
- const { runSearchFlow } = await import("./index-CKY11DzK.js");
32314
+ const { runSearchFlow } = await import("./index-CYEise6v.js");
32063
32315
  const { search } = await import("./file-watcher-Dlx0PolG.js").then((n) => n.B);
32064
32316
  const { tokenize } = await import("./file-watcher-Dlx0PolG.js").then((n) => n.A);
32065
32317
  checkTTY();
@@ -32078,7 +32330,7 @@ async function executeInteractiveSearch(options, context, config2) {
32078
32330
  })
32079
32331
  );
32080
32332
  if (result.selectedItems && !result.cancelled) {
32081
- const { isSideEffectAction } = await import("./action-menu-ByDpq-KF.js");
32333
+ const { isSideEffectAction } = await import("./action-menu-DD0RtNVD.js");
32082
32334
  if (isSideEffectAction(result.action)) {
32083
32335
  await executeSideEffectAction(result.action, result.selectedItems, context, config2);
32084
32336
  return { output: "", cancelled: false, action: result.action };
@@ -32094,7 +32346,7 @@ async function executeSideEffectAction(action, items2, context, config2) {
32094
32346
  switch (action) {
32095
32347
  case "open-url": {
32096
32348
  const { resolveDefaultUrl: resolveDefaultUrl2 } = await Promise.resolve().then(() => url);
32097
- const { openWithSystemApp: openWithSystemApp2 } = await import("./loader-CV71qNY2.js").then((n) => n.j);
32349
+ const { openWithSystemApp: openWithSystemApp2 } = await import("./loader-B-fte1uv.js").then((n) => n.j);
32098
32350
  const item = items2[0];
32099
32351
  if (!item) return;
32100
32352
  const url$1 = resolveDefaultUrl2(item);
@@ -32481,7 +32733,7 @@ function formatUpdateOutput(result, identifier) {
32481
32733
  }
32482
32734
  async function executeInteractiveUpdate(context, config2) {
32483
32735
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
32484
- const { selectReferencesOrExit } = await import("./reference-select-CrOVXP7v.js");
32736
+ const { selectReferencesOrExit } = await import("./reference-select-Qpgt9cbN.js");
32485
32737
  const allReferences = await context.library.getAll();
32486
32738
  const identifiers = await withAlternateScreen2(
32487
32739
  () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -32776,7 +33028,7 @@ function getUrlExitCode(result) {
32776
33028
  }
32777
33029
  async function executeInteractiveSelect(context, config2) {
32778
33030
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
32779
- const { selectReferencesOrExit } = await import("./reference-select-CrOVXP7v.js");
33031
+ const { selectReferencesOrExit } = await import("./reference-select-Qpgt9cbN.js");
32780
33032
  const allReferences = await context.library.getAll();
32781
33033
  const identifiers = await withAlternateScreen2(
32782
33034
  () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -33102,6 +33354,33 @@ function registerCompletionCommand(program) {
33102
33354
  }
33103
33355
  });
33104
33356
  }
33357
+ function buildSearchHelpText() {
33358
+ return `
33359
+ QUERY SYNTAX
33360
+ Free text machine learning Search all fields (AND logic)
33361
+ Phrase "machine learning" Exact phrase match
33362
+ Field author:Smith Search specific field
33363
+ Field+Phrase author:"John Smith" Field with phrase
33364
+
33365
+ FIELDS
33366
+ author, title, year, doi, pmid, pmcid, isbn, url, keyword, tag, id
33367
+
33368
+ CASE SENSITIVITY
33369
+ Consecutive uppercase (2+ letters) is case-sensitive:
33370
+ AI → matches "AI therapy", not "ai therapy"
33371
+ RNA → matches "mRNA synthesis", not "mrna synthesis"
33372
+ Other text is case-insensitive:
33373
+ api → matches "API", "api", "Api"
33374
+
33375
+ EXAMPLES
33376
+ $ ref search "machine learning"
33377
+ $ ref search author:Smith year:2020
33378
+ $ ref search author:"John Smith" title:introduction
33379
+ $ ref search tag:review --sort published --order desc
33380
+ $ ref search AI therapy # AI is case-sensitive
33381
+ $ ref search id:smith2023 --output json
33382
+ $ ref search --tui # Interactive mode`;
33383
+ }
33105
33384
  function createProgram() {
33106
33385
  const program = new Command();
33107
33386
  program.name("reference-manager").version(packageJson.version).description(packageJson.description);
@@ -33120,6 +33399,7 @@ function createProgram() {
33120
33399
  registerRemoveCommand(program);
33121
33400
  registerUpdateCommand(program);
33122
33401
  registerEditCommand(program);
33402
+ registerCheckCommand(program);
33123
33403
  registerCiteCommand(program);
33124
33404
  registerServerCommand(program);
33125
33405
  registerFulltextCommand(program);
@@ -33219,7 +33499,9 @@ async function handleSearchAction(query, options, program) {
33219
33499
  }
33220
33500
  }
33221
33501
  function registerSearchCommand(program) {
33222
- program.command("search").description("Search references").argument("[query]", "Search query (required unless using --tui)").option("-t, --tui", "Enable TUI (interactive) search mode").option(
33502
+ program.command("search").description(
33503
+ "Search references in the library.\n\nSupports field-specific search with field:value syntax and phrase search\nwith quoted strings. Consecutive uppercase letters (AI, RNA) are matched\ncase-sensitively; other text is matched case-insensitively."
33504
+ ).argument("[query]", "Search query (required unless using --tui)").addHelpText("after", buildSearchHelpText()).option("-t, --tui", "Enable TUI (interactive) search mode").option(
33223
33505
  "-o, --output <format>",
33224
33506
  "Output format: pretty|json|bibtex|ids|uuid|pandoc-key|latex-key"
33225
33507
  ).option("--json", "Alias for --output json").option("--bibtex", "Alias for --output bibtex").option("--ids-only", "Alias for --output ids").option("--uuid-only", "Alias for --output uuid").option("-k, --key", "Output citation keys (uses citation.default_key_format config)").option("--pandoc-key", "Alias for --output pandoc-key").option("--latex-key", "Alias for --output latex-key").option("--sort <field>", "Sort by field: created|updated|published|author|title|relevance").option("--order <order>", "Sort order: asc|desc").option("-n, --limit <n>", "Maximum number of results", Number.parseInt).option("--offset <n>", "Number of results to skip", Number.parseInt).action(async (query, options) => {
@@ -33279,7 +33561,7 @@ function shouldAutoFetch(cliFlag, configEnabled) {
33279
33561
  return configEnabled;
33280
33562
  }
33281
33563
  async function performAutoFetch(addedItems, context, config2) {
33282
- const { fulltextFetch: fulltextFetch2 } = await import("./index-DWAtvFtp.js").then((n) => n.v);
33564
+ const { fulltextFetch: fulltextFetch2 } = await import("./index-D2HsxXnK.js").then((n) => n.w);
33283
33565
  const fetchResults = await autoFetchFulltext(addedItems, context, {
33284
33566
  fulltextConfig: config2.fulltext,
33285
33567
  fulltextDirectory: config2.attachments.directory,
@@ -33359,6 +33641,11 @@ function registerEditCommand(program) {
33359
33641
  await handleEditAction(identifiers, options, program.opts());
33360
33642
  });
33361
33643
  }
33644
+ function registerCheckCommand(program) {
33645
+ program.command("check").description("Check references for retractions, expressions of concern, and version changes").argument("[ids...]", "Citation keys or UUIDs to check (interactive selection if omitted)").option("--all", "Check all references in library").option("--search <query>", "Check references matching search query").option("--uuid", "Interpret identifiers as UUIDs").option("-o, --output <format>", "Output format: text|json", "text").option("--full", "Include full details in JSON output").option("--no-save", "Report only, do not save results to library").option("--days <n>", "Skip references checked within n days (default: 7)", Number.parseInt).option("--fix", "Interactive repair for findings (TTY only)").action(async (ids, options) => {
33646
+ await handleCheckAction(ids, options, program.opts());
33647
+ });
33648
+ }
33362
33649
  function registerCiteCommand(program) {
33363
33650
  program.command("cite").description("Generate formatted citations for references").argument(
33364
33651
  "[id-or-uuid...]",
@@ -33558,4 +33845,4 @@ export {
33558
33845
  restoreStdinAfterInk as r,
33559
33846
  syncAttachments as s
33560
33847
  };
33561
- //# sourceMappingURL=index-ChvsE9WF.js.map
33848
+ //# sourceMappingURL=index-QTYx5RaF.js.map