@ncukondo/reference-manager 0.25.0 → 0.27.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 (61) hide show
  1. package/README.md +14 -2
  2. package/dist/chunks/{SearchableMultiSelect-B7qEWPDT.js → SearchableMultiSelect-D6-pj_XI.js} +2 -2
  3. package/dist/chunks/{SearchableMultiSelect-B7qEWPDT.js.map → SearchableMultiSelect-D6-pj_XI.js.map} +1 -1
  4. package/dist/chunks/{action-menu-DD0RtNVD.js → action-menu-DP1rmCkH.js} +3 -3
  5. package/dist/chunks/{action-menu-DD0RtNVD.js.map → action-menu-DP1rmCkH.js.map} +1 -1
  6. package/dist/chunks/checker-B-SL7krG.js +170 -0
  7. package/dist/chunks/checker-B-SL7krG.js.map +1 -0
  8. package/dist/chunks/crossref-client-D6g3pLUI.js +94 -0
  9. package/dist/chunks/crossref-client-D6g3pLUI.js.map +1 -0
  10. package/dist/chunks/{fix-interaction-BpfMLRNY.js → fix-interaction-CTIvq9t4.js} +79 -20
  11. package/dist/chunks/fix-interaction-CTIvq9t4.js.map +1 -0
  12. package/dist/chunks/{index-QTYx5RaF.js → index-B8ST0WLa.js} +235 -79
  13. package/dist/chunks/index-B8ST0WLa.js.map +1 -0
  14. package/dist/chunks/{index-CYEise6v.js → index-Bo1JIDmF.js} +4 -4
  15. package/dist/chunks/{index-CYEise6v.js.map → index-Bo1JIDmF.js.map} +1 -1
  16. package/dist/chunks/{index-D2HsxXnK.js → index-DUpYvm-W.js} +43 -25
  17. package/dist/chunks/index-DUpYvm-W.js.map +1 -0
  18. package/dist/chunks/{index-PQkbePWV.js → index-F4gbDFWf.js} +3 -3
  19. package/dist/chunks/index-F4gbDFWf.js.map +1 -0
  20. package/dist/chunks/metadata-comparator-C5zfoYdK.js +137 -0
  21. package/dist/chunks/metadata-comparator-C5zfoYdK.js.map +1 -0
  22. package/dist/chunks/{pubmed-client-J18fg3fG.js → pubmed-client-mGn5jDIc.js} +2 -2
  23. package/dist/chunks/{pubmed-client-J18fg3fG.js.map → pubmed-client-mGn5jDIc.js.map} +1 -1
  24. package/dist/chunks/{reference-select-Qpgt9cbN.js → reference-select-DtzpiOvp.js} +3 -3
  25. package/dist/chunks/{reference-select-Qpgt9cbN.js.map → reference-select-DtzpiOvp.js.map} +1 -1
  26. package/dist/chunks/{style-select-mEMoWbM2.js → style-select-BT-HOyFf.js} +3 -3
  27. package/dist/chunks/{style-select-mEMoWbM2.js.map → style-select-BT-HOyFf.js.map} +1 -1
  28. package/dist/cli/commands/check.d.ts +2 -1
  29. package/dist/cli/commands/check.d.ts.map +1 -1
  30. package/dist/cli/commands/fulltext.d.ts +12 -7
  31. package/dist/cli/commands/fulltext.d.ts.map +1 -1
  32. package/dist/cli/index.d.ts.map +1 -1
  33. package/dist/cli.js +1 -1
  34. package/dist/features/check/checker.d.ts +1 -0
  35. package/dist/features/check/checker.d.ts.map +1 -1
  36. package/dist/features/check/crossref-client.d.ts +16 -0
  37. package/dist/features/check/crossref-client.d.ts.map +1 -1
  38. package/dist/features/check/fix-actions.d.ts +1 -1
  39. package/dist/features/check/fix-actions.d.ts.map +1 -1
  40. package/dist/features/check/fix-interaction.d.ts.map +1 -1
  41. package/dist/features/check/metadata-comparator.d.ts +37 -0
  42. package/dist/features/check/metadata-comparator.d.ts.map +1 -0
  43. package/dist/features/check/metadata-similarity.d.ts +22 -0
  44. package/dist/features/check/metadata-similarity.d.ts.map +1 -0
  45. package/dist/features/check/types.d.ts +6 -1
  46. package/dist/features/check/types.d.ts.map +1 -1
  47. package/dist/features/operations/check.d.ts +1 -0
  48. package/dist/features/operations/check.d.ts.map +1 -1
  49. package/dist/mcp/tools/check.d.ts +1 -0
  50. package/dist/mcp/tools/check.d.ts.map +1 -1
  51. package/dist/server/routes/check.d.ts.map +1 -1
  52. package/dist/server.js +1 -1
  53. package/package.json +1 -1
  54. package/dist/chunks/checker-7pzK2XSC.js +0 -92
  55. package/dist/chunks/checker-7pzK2XSC.js.map +0 -1
  56. package/dist/chunks/crossref-client-DGNz4PNW.js +0 -52
  57. package/dist/chunks/crossref-client-DGNz4PNW.js.map +0 -1
  58. package/dist/chunks/fix-interaction-BpfMLRNY.js.map +0 -1
  59. package/dist/chunks/index-D2HsxXnK.js.map +0 -1
  60. package/dist/chunks/index-PQkbePWV.js.map +0 -1
  61. package/dist/chunks/index-QTYx5RaF.js.map +0 -1
@@ -5,7 +5,7 @@ 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, 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";
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 fulltextDiscover, m as fulltextFetch, o as fulltextConvert, q as fulltextGet, r as getExtension, s as getDefaultExportFromCjs, B as BUILTIN_STYLES, t as getFulltextAttachmentTypes, u as startServerWithFileWatcher } from "./index-DUpYvm-W.js";
9
9
  import { readFile, unlink, stat, readdir, rename } from "node:fs/promises";
10
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";
@@ -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.25.0";
22
+ const version$1 = "0.27.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-D2HsxXnK.js").then((n) => n.E);
905
+ const { searchReferences } = await import("./index-DUpYvm-W.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-D2HsxXnK.js").then((n) => n.D);
909
+ const { listReferences } = await import("./index-DUpYvm-W.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-D2HsxXnK.js").then((n) => n.C);
913
+ const { citeReferences } = await import("./index-DUpYvm-W.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,36 +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-D2HsxXnK.js").then((n) => n.z);
924
+ const { addReferences } = await import("./index-DUpYvm-W.js").then((n) => n.z);
925
925
  return addReferences(inputs, this.library, options ?? {});
926
926
  }
927
927
  async check(options) {
928
- const { checkReferences } = await import("./index-D2HsxXnK.js").then((n) => n.A);
928
+ const { checkReferences } = await import("./index-DUpYvm-W.js").then((n) => n.A);
929
929
  return checkReferences(this.library, options);
930
930
  }
931
931
  // Attachment operations
932
932
  async attachAdd(options) {
933
- const { addAttachment: addAttachment2 } = await import("./index-PQkbePWV.js");
933
+ const { addAttachment: addAttachment2 } = await import("./index-F4gbDFWf.js");
934
934
  return addAttachment2(this.library, options);
935
935
  }
936
936
  async attachList(options) {
937
- const { listAttachments: listAttachments2 } = await import("./index-PQkbePWV.js");
937
+ const { listAttachments: listAttachments2 } = await import("./index-F4gbDFWf.js");
938
938
  return listAttachments2(this.library, options);
939
939
  }
940
940
  async attachGet(options) {
941
- const { getAttachment: getAttachment2 } = await import("./index-PQkbePWV.js");
941
+ const { getAttachment: getAttachment2 } = await import("./index-F4gbDFWf.js");
942
942
  return getAttachment2(this.library, options);
943
943
  }
944
944
  async attachDetach(options) {
945
- const { detachAttachment: detachAttachment2 } = await import("./index-PQkbePWV.js");
945
+ const { detachAttachment: detachAttachment2 } = await import("./index-F4gbDFWf.js");
946
946
  return detachAttachment2(this.library, options);
947
947
  }
948
948
  async attachSync(options) {
949
- const { syncAttachments: syncAttachments2 } = await import("./index-PQkbePWV.js");
949
+ const { syncAttachments: syncAttachments2 } = await import("./index-F4gbDFWf.js");
950
950
  return syncAttachments2(this.library, options);
951
951
  }
952
952
  async attachOpen(options) {
953
- const { openAttachment: openAttachment2 } = await import("./index-PQkbePWV.js");
953
+ const { openAttachment: openAttachment2 } = await import("./index-F4gbDFWf.js");
954
954
  return openAttachment2(this.library, options);
955
955
  }
956
956
  }
@@ -1806,7 +1806,7 @@ function getAttachExitCode(result) {
1806
1806
  }
1807
1807
  async function executeInteractiveSelect$2(context, config2) {
1808
1808
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
1809
- const { selectReferencesOrExit } = await import("./reference-select-Qpgt9cbN.js");
1809
+ const { selectReferencesOrExit } = await import("./reference-select-DtzpiOvp.js");
1810
1810
  const allReferences = await context.library.getAll();
1811
1811
  const identifiers = await withAlternateScreen2(
1812
1812
  () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -2312,9 +2312,12 @@ function buildCheckOptions(options, appConfig) {
2312
2312
  if (options.days !== void 0) {
2313
2313
  opOptions.skipDays = options.days;
2314
2314
  }
2315
- if (options.noSave) {
2315
+ if (options.save === false) {
2316
2316
  opOptions.save = false;
2317
2317
  }
2318
+ if (options.metadata === false) {
2319
+ opOptions.metadata = false;
2320
+ }
2318
2321
  if (appConfig) {
2319
2322
  const pubmed = {};
2320
2323
  if (appConfig.pubmed.email) pubmed.email = appConfig.pubmed.email;
@@ -2332,45 +2335,109 @@ async function executeCheck(options, context, appConfig) {
2332
2335
  function getStatusLabel(result) {
2333
2336
  if (result.status === "skipped") return "[SKIPPED]";
2334
2337
  if (result.status === "ok") return "[OK]";
2335
- const finding = result.findings[0];
2336
- if (!finding) return "[WARNING]";
2337
- switch (finding.type) {
2338
+ if (result.findings.length === 0) return "[WARNING]";
2339
+ const priorityOrder = {
2340
+ retracted: 0,
2341
+ concern: 1,
2342
+ version_changed: 2,
2343
+ metadata_mismatch: 3,
2344
+ metadata_outdated: 4
2345
+ };
2346
+ let highestPriority = Number.MAX_SAFE_INTEGER;
2347
+ let highestType = "unknown";
2348
+ for (const finding of result.findings) {
2349
+ const priority = priorityOrder[finding.type] ?? 99;
2350
+ if (priority < highestPriority) {
2351
+ highestPriority = priority;
2352
+ highestType = finding.type;
2353
+ }
2354
+ }
2355
+ switch (highestType) {
2338
2356
  case "retracted":
2339
2357
  return "[RETRACTED]";
2340
2358
  case "concern":
2341
2359
  return "[CONCERN]";
2342
2360
  case "version_changed":
2343
2361
  return "[VERSION]";
2344
- case "metadata_changed":
2345
- return "[METADATA]";
2362
+ case "metadata_mismatch":
2363
+ return "[MISMATCH]";
2364
+ case "metadata_outdated":
2365
+ return "[OUTDATED]";
2346
2366
  default:
2347
2367
  return "[WARNING]";
2348
2368
  }
2349
2369
  }
2350
- function formatFindingDetails(finding) {
2370
+ function formatFieldDiff(diff) {
2371
+ const local = diff.local ?? "(none)";
2372
+ const remote = diff.remote ?? "(none)";
2373
+ return ` ${diff.field}: "${local}" → "${remote}"`;
2374
+ }
2375
+ function formatFindingDetails(finding, refId) {
2351
2376
  const lines = [];
2352
- lines.push(` ${finding.message}`);
2353
- if (finding.details?.retractionDoi) {
2354
- lines.push(` Retraction notice: https://doi.org/${finding.details.retractionDoi}`);
2377
+ if (finding.details?.fieldDiffs && finding.details.fieldDiffs.length > 0) {
2378
+ for (const diff of finding.details.fieldDiffs) {
2379
+ lines.push(formatFieldDiff(diff));
2380
+ }
2355
2381
  }
2356
- if (finding.details?.newDoi) {
2357
- lines.push(` New DOI: https://doi.org/${finding.details.newDoi}`);
2382
+ if (finding.type === "metadata_mismatch" || finding.type === "metadata_outdated") {
2383
+ const icon = finding.type === "metadata_mismatch" ? "⚠" : "ℹ";
2384
+ lines.push(` ${icon} ${finding.message}`);
2385
+ lines.push(` → Run: ref update ${refId}`);
2386
+ } else {
2387
+ lines.push(` ${finding.message}`);
2388
+ if (finding.details?.retractionDoi) {
2389
+ lines.push(` Retraction notice: https://doi.org/${finding.details.retractionDoi}`);
2390
+ }
2391
+ if (finding.details?.newDoi) {
2392
+ lines.push(` New DOI: https://doi.org/${finding.details.newDoi}`);
2393
+ }
2358
2394
  }
2359
2395
  return lines;
2360
2396
  }
2397
+ const FINDING_TYPE_KEYS = {
2398
+ retracted: "retracted",
2399
+ concern: "concern",
2400
+ metadata_mismatch: "mismatch",
2401
+ metadata_outdated: "outdated",
2402
+ version_changed: "versionChanged"
2403
+ };
2404
+ function countFindingTypes(result) {
2405
+ const counts = {};
2406
+ const allFindings = result.results.flatMap((r) => r.findings);
2407
+ for (const f of allFindings) {
2408
+ const key = FINDING_TYPE_KEYS[f.type] ?? f.type;
2409
+ counts[key] = (counts[key] ?? 0) + 1;
2410
+ }
2411
+ return counts;
2412
+ }
2361
2413
  function formatCheckTextOutput(result) {
2362
2414
  const lines = [];
2363
2415
  for (const r of result.results) {
2364
2416
  const label = getStatusLabel(r);
2365
2417
  lines.push(`${label} ${r.id}`);
2366
2418
  for (const finding of r.findings) {
2367
- lines.push(...formatFindingDetails(finding));
2419
+ lines.push(...formatFindingDetails(finding, r.id));
2368
2420
  }
2369
2421
  lines.push("");
2370
2422
  }
2371
2423
  const { summary } = result;
2372
2424
  const parts = [`${summary.total} checked`];
2373
- if (summary.warnings > 0) parts.push(`${summary.warnings} warning(s)`);
2425
+ const fc = countFindingTypes(result);
2426
+ const summaryItems = [
2427
+ ["retracted", "retracted"],
2428
+ ["concern", "concern"],
2429
+ ["mismatch", "mismatch"],
2430
+ ["outdated", "outdated"],
2431
+ ["versionChanged", "version changed"]
2432
+ ];
2433
+ const knownKeys = new Set(summaryItems.map(([key]) => key));
2434
+ for (const [key, label] of summaryItems) {
2435
+ const count = fc[key] ?? 0;
2436
+ if (count > 0) parts.push(`${count} ${label}`);
2437
+ }
2438
+ for (const [key, count] of Object.entries(fc)) {
2439
+ if (!knownKeys.has(key) && count > 0) parts.push(`${count} ${key}`);
2440
+ }
2374
2441
  if (summary.ok > 0) parts.push(`${summary.ok} ok`);
2375
2442
  if (summary.skipped > 0) parts.push(`${summary.skipped} skipped`);
2376
2443
  lines.push(`Summary: ${parts.join(", ")}`);
@@ -2404,7 +2471,7 @@ async function handleCheckAction(identifiers, options, globalOpts) {
2404
2471
  const jsonOptions = buildJsonOptionsFromRefs(options, outputFormat, result, allRefs);
2405
2472
  outputCheckResult(result, outputFormat, jsonOptions);
2406
2473
  if (options.fix && result.summary.warnings > 0 && allRefs) {
2407
- const { runFixInteraction } = await import("./fix-interaction-BpfMLRNY.js");
2474
+ const { runFixInteraction } = await import("./fix-interaction-CTIvq9t4.js");
2408
2475
  const findItem = (id2) => allRefs.find((item) => item.id === id2);
2409
2476
  const fixResult = await runFixInteraction(result.results, context.library, findItem);
2410
2477
  const removedSuffix = fixResult.removed.length > 0 ? `, ${fixResult.removed.length} removed` : "";
@@ -2472,7 +2539,7 @@ function outputCheckError(error, format2) {
2472
2539
  }
2473
2540
  async function selectReferencesInteractively(context, config2) {
2474
2541
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
2475
- const { selectReferenceItemsOrExit } = await import("./reference-select-Qpgt9cbN.js");
2542
+ const { selectReferenceItemsOrExit } = await import("./reference-select-DtzpiOvp.js");
2476
2543
  const allReferences = await context.library.getAll();
2477
2544
  if (allReferences.length === 0) {
2478
2545
  process.stderr.write("No references in library.\n");
@@ -2540,8 +2607,8 @@ function getCiteExitCode(result) {
2540
2607
  }
2541
2608
  async function executeInteractiveCite(options, context, config2) {
2542
2609
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
2543
- const { runCiteFlow } = await import("./index-CYEise6v.js");
2544
- const { buildStyleChoices, listCustomStyles } = await import("./style-select-mEMoWbM2.js");
2610
+ const { runCiteFlow } = await import("./index-Bo1JIDmF.js");
2611
+ const { buildStyleChoices, listCustomStyles } = await import("./style-select-BT-HOyFf.js");
2545
2612
  const { search } = await import("./file-watcher-Dlx0PolG.js").then((n) => n.B);
2546
2613
  const { tokenize } = await import("./file-watcher-Dlx0PolG.js").then((n) => n.A);
2547
2614
  const { checkTTY } = await import("./tty-BMyaEOhX.js");
@@ -7142,7 +7209,7 @@ function formatEditOutput(result) {
7142
7209
  }
7143
7210
  async function executeInteractiveEdit(options, context, config2) {
7144
7211
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
7145
- const { selectReferencesOrExit } = await import("./reference-select-Qpgt9cbN.js");
7212
+ const { selectReferencesOrExit } = await import("./reference-select-DtzpiOvp.js");
7146
7213
  const allReferences = await context.library.getAll();
7147
7214
  const identifiers = await withAlternateScreen2(
7148
7215
  () => selectReferencesOrExit(allReferences, { multiSelect: true }, config2.cli.tui)
@@ -10681,14 +10748,14 @@ function formatFulltextConvertOutput(result) {
10681
10748
  function getFulltextExitCode(result) {
10682
10749
  return result.success ? 0 : 1;
10683
10750
  }
10684
- async function executeInteractiveSelect$1(context, config2) {
10751
+ async function executeInteractiveSelect$1(context, config2, multiSelect = false) {
10685
10752
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
10686
- const { selectReferencesOrExit } = await import("./reference-select-Qpgt9cbN.js");
10753
+ const { selectReferencesOrExit } = await import("./reference-select-DtzpiOvp.js");
10687
10754
  const allReferences = await context.library.getAll();
10688
10755
  const identifiers = await withAlternateScreen2(
10689
- () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
10756
+ () => selectReferencesOrExit(allReferences, { multiSelect }, config2.cli.tui)
10690
10757
  );
10691
- return identifiers[0];
10758
+ return identifiers;
10692
10759
  }
10693
10760
  function isValidFilePath(value) {
10694
10761
  return typeof value === "string" && value !== "" && value !== "true";
@@ -10720,7 +10787,7 @@ async function handleFulltextAttachAction(identifierArg, filePathArg, options, g
10720
10787
  setExitCode(ExitCode.ERROR);
10721
10788
  return;
10722
10789
  }
10723
- identifier = await executeInteractiveSelect$1(context, config2);
10790
+ identifier = (await executeInteractiveSelect$1(context, config2))[0];
10724
10791
  }
10725
10792
  const { type: type2, filePath } = parseFulltextAttachTypeAndPath(filePathArg, options);
10726
10793
  const stdinContent = !filePath && type2 ? await readStdinBuffer() : void 0;
@@ -10745,6 +10812,48 @@ async function handleFulltextAttachAction(identifierArg, filePathArg, options, g
10745
10812
  setExitCode(ExitCode.INTERNAL_ERROR);
10746
10813
  }
10747
10814
  }
10815
+ function formatMultiFulltextGetOutput(results) {
10816
+ const isSingle = results.length === 1;
10817
+ if (isSingle) {
10818
+ const { result } = results[0];
10819
+ const formatted = formatFulltextGetOutput(result);
10820
+ return {
10821
+ stdout: result.success ? formatted : "",
10822
+ stderr: result.success ? "" : formatted
10823
+ };
10824
+ }
10825
+ const stdoutLines = [];
10826
+ const stderrLines = [];
10827
+ for (const { id: id2, result } of results) {
10828
+ if (result.success) {
10829
+ stdoutLines.push(`${id2}:`);
10830
+ if (result.paths?.pdf) {
10831
+ stdoutLines.push(` pdf: ${result.paths.pdf}`);
10832
+ }
10833
+ if (result.paths?.markdown) {
10834
+ stdoutLines.push(` markdown: ${result.paths.markdown}`);
10835
+ }
10836
+ } else {
10837
+ stderrLines.push(`Error: ${result.error}`);
10838
+ }
10839
+ }
10840
+ return {
10841
+ stdout: stdoutLines.join("\n"),
10842
+ stderr: stderrLines.join("\n")
10843
+ };
10844
+ }
10845
+ function formatFulltextGetJsonOutput(results) {
10846
+ const toJsonItem = ({ id: id2, result }) => {
10847
+ if (result.success) {
10848
+ return { id: id2, success: true, paths: result.paths };
10849
+ }
10850
+ return { id: id2, success: false, error: result.error };
10851
+ };
10852
+ if (results.length === 1) {
10853
+ return JSON.stringify(toJsonItem(results[0]), null, 2);
10854
+ }
10855
+ return JSON.stringify(results.map(toJsonItem), null, 2);
10856
+ }
10748
10857
  function outputFulltextGetResult(result, useStdout) {
10749
10858
  if (result.success && result.content && useStdout) {
10750
10859
  process.stdout.write(result.content);
@@ -10759,27 +10868,26 @@ function outputFulltextGetResult(result, useStdout) {
10759
10868
  `);
10760
10869
  }
10761
10870
  }
10762
- async function handleFulltextGetAction(identifierArg, options, globalOpts) {
10763
- try {
10764
- const config2 = await loadConfigWithOverrides({ ...globalOpts, ...options });
10765
- const context = await createExecutionContext(config2, Library.load);
10766
- let identifier;
10767
- if (identifierArg) {
10768
- identifier = identifierArg;
10769
- } else if (isTTY()) {
10770
- identifier = await executeInteractiveSelect$1(context, config2);
10771
- } else {
10772
- const stdinId = await readIdentifierFromStdin();
10773
- if (!stdinId) {
10774
- process.stderr.write(
10775
- "Error: No identifier provided. Provide an ID, pipe one via stdin, or run interactively in a TTY.\n"
10776
- );
10777
- setExitCode(ExitCode.ERROR);
10778
- return;
10779
- }
10780
- identifier = stdinId;
10781
- }
10782
- const preferValue = options.prefer ?? config2.fulltext.preferredType;
10871
+ async function resolveGetIdentifiers(identifierArgs, context, config2) {
10872
+ if (identifierArgs.length > 0) {
10873
+ return identifierArgs;
10874
+ }
10875
+ if (isTTY()) {
10876
+ return executeInteractiveSelect$1(context, config2, true);
10877
+ }
10878
+ const stdinIds = await readIdentifiersFromStdin();
10879
+ if (stdinIds.length === 0) {
10880
+ process.stderr.write(
10881
+ "Error: No identifier provided. Provide an ID, pipe one via stdin, or run interactively in a TTY.\n"
10882
+ );
10883
+ return null;
10884
+ }
10885
+ return stdinIds;
10886
+ }
10887
+ async function collectFulltextGetResults(identifiers, options, config2, context) {
10888
+ const preferValue = options.prefer ?? config2.fulltext.preferredType;
10889
+ const results = [];
10890
+ for (const identifier of identifiers) {
10783
10891
  const getOptions = {
10784
10892
  identifier,
10785
10893
  fulltextDirectory: config2.attachments.directory,
@@ -10790,8 +10898,48 @@ async function handleFulltextGetAction(identifierArg, options, globalOpts) {
10790
10898
  ...preferValue && { preferredType: preferValue }
10791
10899
  };
10792
10900
  const result = await executeFulltextGet(getOptions, context);
10793
- outputFulltextGetResult(result, Boolean(options.stdout));
10794
- setExitCode(getFulltextExitCode(result));
10901
+ results.push({ id: identifier, result });
10902
+ }
10903
+ return results;
10904
+ }
10905
+ function outputFulltextGetResults(results, options) {
10906
+ if (options.output === "json") {
10907
+ process.stdout.write(`${formatFulltextGetJsonOutput(results)}
10908
+ `);
10909
+ return;
10910
+ }
10911
+ if (results.length === 1) {
10912
+ outputFulltextGetResult(results[0].result, Boolean(options.stdout));
10913
+ return;
10914
+ }
10915
+ const output = formatMultiFulltextGetOutput(results);
10916
+ if (output.stdout) {
10917
+ process.stdout.write(`${output.stdout}
10918
+ `);
10919
+ }
10920
+ if (output.stderr) {
10921
+ process.stderr.write(`${output.stderr}
10922
+ `);
10923
+ }
10924
+ }
10925
+ async function handleFulltextGetAction(identifierArgs, options, globalOpts) {
10926
+ try {
10927
+ const config2 = await loadConfigWithOverrides({ ...globalOpts, ...options });
10928
+ const context = await createExecutionContext(config2, Library.load);
10929
+ const identifiers = await resolveGetIdentifiers(identifierArgs, context, config2);
10930
+ if (!identifiers) {
10931
+ setExitCode(ExitCode.ERROR);
10932
+ return;
10933
+ }
10934
+ if (options.stdout && identifiers.length > 1) {
10935
+ process.stderr.write("Error: --stdout cannot be used with multiple identifiers\n");
10936
+ setExitCode(ExitCode.ERROR);
10937
+ return;
10938
+ }
10939
+ const results = await collectFulltextGetResults(identifiers, options, config2, context);
10940
+ outputFulltextGetResults(results, options);
10941
+ const hasFailure = results.some((r) => !r.result.success);
10942
+ setExitCode(hasFailure ? ExitCode.ERROR : ExitCode.SUCCESS);
10795
10943
  } catch (error) {
10796
10944
  process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}
10797
10945
  `);
@@ -10806,7 +10954,7 @@ async function handleFulltextDetachAction(identifierArg, options, globalOpts) {
10806
10954
  if (identifierArg) {
10807
10955
  identifier = identifierArg;
10808
10956
  } else if (isTTY()) {
10809
- identifier = await executeInteractiveSelect$1(context, config2);
10957
+ identifier = (await executeInteractiveSelect$1(context, config2))[0];
10810
10958
  } else {
10811
10959
  const stdinId = await readIdentifierFromStdin();
10812
10960
  if (!stdinId) {
@@ -10846,7 +10994,7 @@ async function handleFulltextOpenAction(identifierArg, options, globalOpts) {
10846
10994
  if (identifierArg) {
10847
10995
  identifier = identifierArg;
10848
10996
  } else if (isTTY()) {
10849
- identifier = await executeInteractiveSelect$1(context, config2);
10997
+ identifier = (await executeInteractiveSelect$1(context, config2))[0];
10850
10998
  } else {
10851
10999
  const stdinId = await readIdentifierFromStdin();
10852
11000
  if (!stdinId) {
@@ -10886,7 +11034,7 @@ async function handleFulltextDiscoverAction(identifierArg, options, globalOpts)
10886
11034
  if (identifierArg) {
10887
11035
  identifier = identifierArg;
10888
11036
  } else if (isTTY()) {
10889
- identifier = await executeInteractiveSelect$1(context, config2);
11037
+ identifier = (await executeInteractiveSelect$1(context, config2))[0];
10890
11038
  } else {
10891
11039
  const stdinId = await readIdentifierFromStdin();
10892
11040
  if (!stdinId) {
@@ -10926,7 +11074,7 @@ async function handleFulltextFetchAction(identifierArg, options, globalOpts) {
10926
11074
  if (identifierArg) {
10927
11075
  identifier = identifierArg;
10928
11076
  } else if (isTTY()) {
10929
- identifier = await executeInteractiveSelect$1(context, config2);
11077
+ identifier = (await executeInteractiveSelect$1(context, config2))[0];
10930
11078
  } else {
10931
11079
  const stdinId = await readIdentifierFromStdin();
10932
11080
  if (!stdinId) {
@@ -10966,7 +11114,7 @@ async function handleFulltextConvertAction(identifierArg, options, globalOpts) {
10966
11114
  if (identifierArg) {
10967
11115
  identifier = identifierArg;
10968
11116
  } else if (isTTY()) {
10969
- identifier = await executeInteractiveSelect$1(context, config2);
11117
+ identifier = (await executeInteractiveSelect$1(context, config2))[0];
10970
11118
  } else {
10971
11119
  const stdinId = await readIdentifierFromStdin();
10972
11120
  if (!stdinId) {
@@ -11008,8 +11156,10 @@ const fulltext = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
11008
11156
  formatFulltextDetachOutput,
11009
11157
  formatFulltextDiscoverOutput,
11010
11158
  formatFulltextFetchOutput,
11159
+ formatFulltextGetJsonOutput,
11011
11160
  formatFulltextGetOutput,
11012
11161
  formatFulltextOpenOutput,
11162
+ formatMultiFulltextGetOutput,
11013
11163
  getFulltextExitCode,
11014
11164
  handleFulltextAttachAction,
11015
11165
  handleFulltextConvertAction,
@@ -31561,7 +31711,8 @@ function registerCheckTool(server, getLibraryOperations) {
31561
31711
  ids: z.array(z.string()).optional().describe("Array of reference IDs to check. Omit if using 'all'."),
31562
31712
  all: z.boolean().optional().describe("Check all references in library"),
31563
31713
  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)")
31714
+ save: z.boolean().optional().describe("Whether to save results to library (default: true)"),
31715
+ metadata: z.boolean().optional().describe("Compare metadata against remote sources (default: true)")
31565
31716
  }
31566
31717
  },
31567
31718
  async (args) => {
@@ -31571,6 +31722,7 @@ function registerCheckTool(server, getLibraryOperations) {
31571
31722
  if (args.all) options.all = true;
31572
31723
  if (args.skipDays !== void 0) options.skipDays = args.skipDays;
31573
31724
  if (args.save !== void 0) options.save = args.save;
31725
+ if (args.metadata !== void 0) options.metadata = args.metadata;
31574
31726
  const result = await libraryOps.check(options);
31575
31727
  const text = formatCheckResult(result);
31576
31728
  return {
@@ -32031,7 +32183,7 @@ async function mcpStart(options) {
32031
32183
  async function executeRemove(options, context) {
32032
32184
  const { identifier, idType = "id", fulltextDirectory, deleteFulltext = false } = options;
32033
32185
  if (context.mode === "local" && deleteFulltext && fulltextDirectory) {
32034
- const { removeReference } = await import("./index-D2HsxXnK.js").then((n) => n.x);
32186
+ const { removeReference } = await import("./index-DUpYvm-W.js").then((n) => n.x);
32035
32187
  return removeReference(context.library, {
32036
32188
  identifier,
32037
32189
  idType,
@@ -32086,7 +32238,7 @@ Continue?`;
32086
32238
  }
32087
32239
  async function executeInteractiveRemove(context, config2) {
32088
32240
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
32089
- const { selectReferenceItemsOrExit } = await import("./reference-select-Qpgt9cbN.js");
32241
+ const { selectReferenceItemsOrExit } = await import("./reference-select-DtzpiOvp.js");
32090
32242
  const allReferences = await context.library.getAll();
32091
32243
  const selectedItems = await withAlternateScreen2(
32092
32244
  () => selectReferenceItemsOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -32311,7 +32463,7 @@ async function executeInteractiveSearch(options, context, config2) {
32311
32463
  validateInteractiveOptions(options);
32312
32464
  const { checkTTY } = await import("./tty-BMyaEOhX.js");
32313
32465
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
32314
- const { runSearchFlow } = await import("./index-CYEise6v.js");
32466
+ const { runSearchFlow } = await import("./index-Bo1JIDmF.js");
32315
32467
  const { search } = await import("./file-watcher-Dlx0PolG.js").then((n) => n.B);
32316
32468
  const { tokenize } = await import("./file-watcher-Dlx0PolG.js").then((n) => n.A);
32317
32469
  checkTTY();
@@ -32330,7 +32482,7 @@ async function executeInteractiveSearch(options, context, config2) {
32330
32482
  })
32331
32483
  );
32332
32484
  if (result.selectedItems && !result.cancelled) {
32333
- const { isSideEffectAction } = await import("./action-menu-DD0RtNVD.js");
32485
+ const { isSideEffectAction } = await import("./action-menu-DP1rmCkH.js");
32334
32486
  if (isSideEffectAction(result.action)) {
32335
32487
  await executeSideEffectAction(result.action, result.selectedItems, context, config2);
32336
32488
  return { output: "", cancelled: false, action: result.action };
@@ -32733,7 +32885,7 @@ function formatUpdateOutput(result, identifier) {
32733
32885
  }
32734
32886
  async function executeInteractiveUpdate(context, config2) {
32735
32887
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
32736
- const { selectReferencesOrExit } = await import("./reference-select-Qpgt9cbN.js");
32888
+ const { selectReferencesOrExit } = await import("./reference-select-DtzpiOvp.js");
32737
32889
  const allReferences = await context.library.getAll();
32738
32890
  const identifiers = await withAlternateScreen2(
32739
32891
  () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -33028,7 +33180,7 @@ function getUrlExitCode(result) {
33028
33180
  }
33029
33181
  async function executeInteractiveSelect(context, config2) {
33030
33182
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
33031
- const { selectReferencesOrExit } = await import("./reference-select-Qpgt9cbN.js");
33183
+ const { selectReferencesOrExit } = await import("./reference-select-DtzpiOvp.js");
33032
33184
  const allReferences = await context.library.getAll();
33033
33185
  const identifiers = await withAlternateScreen2(
33034
33186
  () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -33561,7 +33713,7 @@ function shouldAutoFetch(cliFlag, configEnabled) {
33561
33713
  return configEnabled;
33562
33714
  }
33563
33715
  async function performAutoFetch(addedItems, context, config2) {
33564
- const { fulltextFetch: fulltextFetch2 } = await import("./index-D2HsxXnK.js").then((n) => n.w);
33716
+ const { fulltextFetch: fulltextFetch2 } = await import("./index-DUpYvm-W.js").then((n) => n.w);
33565
33717
  const fetchResults = await autoFetchFulltext(addedItems, context, {
33566
33718
  fulltextConfig: config2.fulltext,
33567
33719
  fulltextDirectory: config2.attachments.directory,
@@ -33642,7 +33794,9 @@ function registerEditCommand(program) {
33642
33794
  });
33643
33795
  }
33644
33796
  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) => {
33797
+ program.command("check").description(
33798
+ "Check references for retractions, expressions of concern, version changes, and metadata drift"
33799
+ ).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)").option("--no-metadata", "Skip metadata comparison against remote sources").action(async (ids, options) => {
33646
33800
  await handleCheckAction(ids, options, program.opts());
33647
33801
  });
33648
33802
  }
@@ -33787,10 +33941,12 @@ function registerFulltextCommand(program) {
33787
33941
  fulltextCmd.command("attach").description("Attach a full-text file to a reference").argument("[identifier]", "Citation key or UUID (interactive selection if omitted)").argument("[file-path]", "Path to the file to attach").option("--pdf [path]", "Attach as PDF (path optional if provided as argument)").option("--markdown [path]", "Attach as Markdown (path optional if provided as argument)").option("--move", "Move file instead of copy").option("-f, --force", "Overwrite existing attachment").option("--uuid", "Interpret identifier as UUID").action(async (identifier, filePath, options) => {
33788
33942
  await handleFulltextAttachAction(identifier, filePath, options, program.opts());
33789
33943
  });
33790
- fulltextCmd.command("get").description("Get full-text file path or content").argument("[identifier]", "Citation key or UUID (interactive selection if omitted)").option("--pdf", "Get PDF file only").option("--markdown", "Get Markdown file only").addOption(
33944
+ fulltextCmd.command("get").description("Get full-text file path or content").argument("[identifiers...]", "Citation keys or UUIDs (interactive selection if omitted)").option("--pdf", "Get PDF file only").option("--markdown", "Get Markdown file only").addOption(
33791
33945
  new Option("--prefer <type>", "Preferred fulltext type").choices(["pdf", "markdown"])
33792
- ).option("--stdout", "Output file content to stdout").option("--uuid", "Interpret identifier as UUID").action(async (identifier, options) => {
33793
- await handleFulltextGetAction(identifier, options, program.opts());
33946
+ ).option("--stdout", "Output file content to stdout").option("--uuid", "Interpret identifiers as UUIDs").addOption(
33947
+ new Option("-o, --output <format>", "Output format: json|text").choices(["json", "text"]).default("text")
33948
+ ).action(async (identifiers, options) => {
33949
+ await handleFulltextGetAction(identifiers, options, program.opts());
33794
33950
  });
33795
33951
  fulltextCmd.command("detach").description("Detach full-text file from a reference").argument("[identifier]", "Citation key or UUID (interactive selection if omitted)").option("--pdf", "Detach PDF only").option("--markdown", "Detach Markdown only").option("--remove-files", "Also delete the file from disk").option("-f, --force", "Skip confirmation for file removal").option("--uuid", "Interpret identifier as UUID").action(async (identifier, options) => {
33796
33952
  await handleFulltextDetachAction(identifier, options, program.opts());
@@ -33845,4 +34001,4 @@ export {
33845
34001
  restoreStdinAfterInk as r,
33846
34002
  syncAttachments as s
33847
34003
  };
33848
- //# sourceMappingURL=index-QTYx5RaF.js.map
34004
+ //# sourceMappingURL=index-B8ST0WLa.js.map