@ncukondo/reference-manager 0.24.1 → 0.26.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 (80) hide show
  1. package/README.md +53 -1
  2. package/dist/chunks/{SearchableMultiSelect-CKRfbnka.js → SearchableMultiSelect-D2IzthN4.js} +2 -2
  3. package/dist/chunks/{SearchableMultiSelect-CKRfbnka.js.map → SearchableMultiSelect-D2IzthN4.js.map} +1 -1
  4. package/dist/chunks/{action-menu-BCgv1m3v.js → action-menu-DWdoHTFh.js} +3 -3
  5. package/dist/chunks/{action-menu-BCgv1m3v.js.map → action-menu-DWdoHTFh.js.map} +1 -1
  6. package/dist/chunks/checker-CKfdG8Ia.js +170 -0
  7. package/dist/chunks/checker-CKfdG8Ia.js.map +1 -0
  8. package/dist/chunks/crossref-client-Gs75LMVf.js +94 -0
  9. package/dist/chunks/crossref-client-Gs75LMVf.js.map +1 -0
  10. package/dist/chunks/fix-interaction-DNXbmlPr.js +262 -0
  11. package/dist/chunks/fix-interaction-DNXbmlPr.js.map +1 -0
  12. package/dist/chunks/{index-JA964gjc.js → index-BEQ4YIXx.js} +356 -27
  13. package/dist/chunks/index-BEQ4YIXx.js.map +1 -0
  14. package/dist/chunks/{index-zafRwZEZ.js → index-k67fQbe4.js} +3 -3
  15. package/dist/chunks/index-k67fQbe4.js.map +1 -0
  16. package/dist/chunks/{index-DWAtvFtp.js → index-of6eJn8N.js} +209 -21
  17. package/dist/chunks/index-of6eJn8N.js.map +1 -0
  18. package/dist/chunks/{index-C36Us_zC.js → index-tdmbNN9b.js} +4 -4
  19. package/dist/chunks/{index-C36Us_zC.js.map → index-tdmbNN9b.js.map} +1 -1
  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/metadata-comparator-C5zfoYdK.js +137 -0
  23. package/dist/chunks/metadata-comparator-C5zfoYdK.js.map +1 -0
  24. package/dist/chunks/pubmed-client-CGReJIOz.js +51 -0
  25. package/dist/chunks/pubmed-client-CGReJIOz.js.map +1 -0
  26. package/dist/chunks/{reference-select-Bbl-roAS.js → reference-select-i1Cnmc16.js} +3 -3
  27. package/dist/chunks/{reference-select-Bbl-roAS.js.map → reference-select-i1Cnmc16.js.map} +1 -1
  28. package/dist/chunks/{style-select-BeAktUts.js → style-select-COnY01qb.js} +3 -3
  29. package/dist/chunks/{style-select-BeAktUts.js.map → style-select-COnY01qb.js.map} +1 -1
  30. package/dist/cli/commands/check.d.ts +43 -0
  31. package/dist/cli/commands/check.d.ts.map +1 -0
  32. package/dist/cli/commands/index.d.ts +2 -0
  33. package/dist/cli/commands/index.d.ts.map +1 -1
  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 +19 -0
  45. package/dist/features/check/checker.d.ts.map +1 -0
  46. package/dist/features/check/crossref-client.d.ts +40 -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/metadata-comparator.d.ts +37 -0
  53. package/dist/features/check/metadata-comparator.d.ts.map +1 -0
  54. package/dist/features/check/metadata-similarity.d.ts +22 -0
  55. package/dist/features/check/metadata-similarity.d.ts.map +1 -0
  56. package/dist/features/check/pubmed-client.d.ts +20 -0
  57. package/dist/features/check/pubmed-client.d.ts.map +1 -0
  58. package/dist/features/check/types.d.ts +28 -0
  59. package/dist/features/check/types.d.ts.map +1 -0
  60. package/dist/features/operations/check.d.ts +29 -0
  61. package/dist/features/operations/check.d.ts.map +1 -0
  62. package/dist/features/operations/index.d.ts +1 -0
  63. package/dist/features/operations/index.d.ts.map +1 -1
  64. package/dist/features/operations/library-operations.d.ts +8 -0
  65. package/dist/features/operations/library-operations.d.ts.map +1 -1
  66. package/dist/features/operations/operations-library.d.ts +2 -0
  67. package/dist/features/operations/operations-library.d.ts.map +1 -1
  68. package/dist/index.js +1 -1
  69. package/dist/mcp/tools/check.d.ts +11 -0
  70. package/dist/mcp/tools/check.d.ts.map +1 -0
  71. package/dist/mcp/tools/index.d.ts.map +1 -1
  72. package/dist/server/index.d.ts.map +1 -1
  73. package/dist/server/routes/check.d.ts +4 -0
  74. package/dist/server/routes/check.d.ts.map +1 -0
  75. package/dist/server.js +3 -3
  76. package/package.json +1 -1
  77. package/dist/chunks/index-DWAtvFtp.js.map +0 -1
  78. package/dist/chunks/index-JA964gjc.js.map +0 -1
  79. package/dist/chunks/index-zafRwZEZ.js.map +0 -1
  80. package/dist/chunks/loader-CV71qNY2.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix-interaction-DNXbmlPr.js","sources":["../../src/features/check/fix-actions.ts","../../src/features/check/fix-interaction.ts"],"sourcesContent":["import type { CslItem } from \"../../core/csl-json/types.js\";\nimport type { ILibrary } from \"../../core/library-interface.js\";\nimport type { CheckFinding } from \"./types.js\";\n\nexport type FixActionType =\n | \"add_retracted_tag\"\n | \"add_retraction_note\"\n | \"remove_from_library\"\n | \"update_from_published\"\n | \"add_version_tag\"\n | \"add_concern_tag\"\n | \"add_concern_note\"\n | \"update_all_fields\"\n | \"update_selected_fields\"\n | \"skip\";\n\nexport interface FixAction {\n type: FixActionType;\n label: string;\n}\n\nexport interface FixActionResult {\n applied: boolean;\n message: string;\n removed?: boolean;\n}\n\nexport function getFixActionsForFinding(finding: CheckFinding): FixAction[] {\n switch (finding.type) {\n case \"retracted\":\n return [\n { type: \"add_retracted_tag\", label: 'Add tag \"retracted\"' },\n { type: \"add_retraction_note\", label: \"Add note with retraction details\" },\n { type: \"remove_from_library\", label: \"Remove from library\" },\n { type: \"skip\", label: \"Skip\" },\n ];\n case \"version_changed\":\n return [\n { type: \"update_from_published\", label: \"Update metadata from published version\" },\n { type: \"add_version_tag\", label: 'Add tag \"has-published-version\"' },\n { type: \"skip\", label: \"Skip\" },\n ];\n case \"concern\":\n return [\n { type: \"add_concern_tag\", label: 'Add tag \"expression-of-concern\"' },\n { type: \"add_concern_note\", label: \"Add note with concern details\" },\n { type: \"skip\", label: \"Skip\" },\n ];\n case \"metadata_mismatch\":\n case \"metadata_outdated\":\n return [\n { type: \"update_all_fields\", label: \"Update all changed fields from remote\" },\n { type: \"skip\", label: \"Skip\" },\n ];\n default:\n return [];\n }\n}\n\nfunction addTag(item: CslItem, tag: string): string[] {\n const existing = (item.custom?.tags as string[] | undefined) ?? [];\n if (existing.includes(tag)) {\n return existing;\n }\n return [...existing, tag];\n}\n\nfunction buildNoteText(prefix: string, finding: CheckFinding): string {\n const parts = [prefix];\n if (finding.details?.retractionDate) {\n parts.push(`Date: ${finding.details.retractionDate}`);\n }\n if (finding.details?.retractionDoi) {\n parts.push(`DOI: ${finding.details.retractionDoi}`);\n }\n return parts.join(\". \");\n}\n\nfunction appendNote(existingNote: string | undefined, newNote: string): string {\n if (existingNote) {\n return `${existingNote}\\n\\n${newNote}`;\n }\n return newNote;\n}\n\nasync function applyTagAction(\n library: ILibrary,\n item: CslItem,\n tag: string\n): Promise<FixActionResult> {\n const tags = addTag(item, tag);\n await library.update(item.id, { custom: { ...item.custom, tags } } as Partial<CslItem>, {\n idType: \"id\",\n });\n await library.save();\n return { applied: true, message: `Added tag \"${tag}\"` };\n}\n\nasync function applyNoteAction(\n library: ILibrary,\n item: CslItem,\n prefix: string,\n finding: CheckFinding\n): Promise<FixActionResult> {\n const noteText = buildNoteText(prefix, finding);\n const note = appendNote(item.note, noteText);\n await library.update(item.id, { note } as Partial<CslItem>, { idType: \"id\" });\n await library.save();\n return { applied: true, message: `Added note: ${noteText}` };\n}\n\nasync function applyUpdateFromPublished(\n library: ILibrary,\n item: CslItem,\n finding: CheckFinding\n): Promise<FixActionResult> {\n const newDoi = finding.details?.newDoi;\n if (!newDoi) {\n return { applied: false, message: \"No published DOI available in finding details\" };\n }\n const { fetchDoi } = await import(\"../import/fetcher.js\");\n const fetchResult = await fetchDoi(newDoi);\n if (!fetchResult.success) {\n return {\n applied: false,\n message: `Failed to fetch metadata for ${newDoi}: ${fetchResult.error}`,\n };\n }\n const { id: _id, custom: _custom, ...metadata } = fetchResult.item;\n await library.update(item.id, metadata as Partial<CslItem>, { idType: \"id\" });\n await library.save();\n return { applied: true, message: `Updated metadata from ${newDoi}` };\n}\n\nasync function applyUpdateAllFields(\n library: ILibrary,\n item: CslItem,\n finding: CheckFinding\n): Promise<FixActionResult> {\n let fetchedItem: CslItem;\n\n if (item.DOI) {\n const { fetchDoi } = await import(\"../import/fetcher.js\");\n const fetchResult = await fetchDoi(item.DOI);\n if (!fetchResult.success) {\n return {\n applied: false,\n message: `Failed to fetch metadata for DOI ${item.DOI}: ${fetchResult.error}`,\n };\n }\n fetchedItem = fetchResult.item;\n } else if (item.PMID) {\n const { fetchPmids } = await import(\"../import/fetcher.js\");\n const results = await fetchPmids([item.PMID], {});\n const result = results[0];\n if (!result || !result.success) {\n return {\n applied: false,\n message: `Failed to fetch metadata for PMID ${item.PMID}: ${result?.error ?? \"unknown error\"}`,\n };\n }\n fetchedItem = result.item;\n } else {\n return { applied: false, message: \"No DOI or PMID available for metadata update\" };\n }\n\n const updatedFields = finding.details?.updatedFields ?? [];\n const updates: Partial<CslItem> = {};\n for (const field of updatedFields) {\n if (field in fetchedItem) {\n (updates as Record<string, unknown>)[field] = (fetchedItem as Record<string, unknown>)[field];\n }\n }\n if (Object.keys(updates).length === 0) {\n return { applied: false, message: \"No fields to update\" };\n }\n await library.update(item.id, updates, { idType: \"id\" });\n await library.save();\n return { applied: true, message: `Updated fields: ${updatedFields.join(\", \")}` };\n}\n\nexport async function applyFixAction(\n library: ILibrary,\n item: CslItem,\n finding: CheckFinding,\n actionType: FixActionType\n): Promise<FixActionResult> {\n switch (actionType) {\n case \"add_retracted_tag\":\n return applyTagAction(library, item, \"retracted\");\n\n case \"add_retraction_note\":\n return applyNoteAction(library, item, \"RETRACTED\", finding);\n\n case \"add_concern_tag\":\n return applyTagAction(library, item, \"expression-of-concern\");\n\n case \"add_concern_note\":\n return applyNoteAction(library, item, \"EXPRESSION OF CONCERN\", finding);\n\n case \"add_version_tag\":\n return applyTagAction(library, item, \"has-published-version\");\n\n case \"remove_from_library\": {\n const removeResult = await library.remove(item.id, { idType: \"id\" });\n if (!removeResult.removed) {\n return { applied: false, message: `Failed to remove ${item.id}` };\n }\n await library.save();\n return { applied: true, message: `Removed ${item.id}`, removed: true };\n }\n\n case \"update_from_published\":\n return applyUpdateFromPublished(library, item, finding);\n\n case \"update_all_fields\":\n return applyUpdateAllFields(library, item, finding);\n\n // Placeholder for programmatic API (MCP/server) use.\n // Individual field selection is not available in interactive CLI mode,\n // but can be used via the MCP tool or HTTP server API.\n case \"update_selected_fields\":\n return { applied: false, message: \"Field selection not available in CLI mode\" };\n\n case \"skip\":\n return { applied: true, message: \"Skipped\" };\n\n default:\n return { applied: false, message: `Unknown action: ${actionType}` };\n }\n}\n","import { render } from \"ink\";\nimport { createElement } from \"react\";\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport type { ILibrary } from \"../../core/library-interface.js\";\nimport { restoreStdinAfterInk } from \"../interactive/alternate-screen.js\";\nimport { Select } from \"../interactive/components/index.js\";\nimport type { SelectOption } from \"../interactive/components/index.js\";\nimport { type FixActionType, applyFixAction, getFixActionsForFinding } from \"./fix-actions.js\";\nimport type { CheckFinding, CheckResult } from \"./types.js\";\n\nexport interface FixInteractionResult {\n totalFindings: number;\n applied: number;\n skipped: number;\n removed: string[];\n}\n\nfunction selectFixAction(\n message: string,\n options: SelectOption<FixActionType>[]\n): Promise<FixActionType | null> {\n return new Promise<FixActionType | null>((resolve) => {\n let selected: FixActionType | null = null;\n\n const { waitUntilExit } = render(\n createElement(Select<FixActionType>, {\n options,\n message,\n onSelect: (value: FixActionType) => {\n selected = value;\n },\n onCancel: () => {\n selected = null;\n },\n })\n );\n\n waitUntilExit()\n .then(() => {\n restoreStdinAfterInk();\n resolve(selected);\n })\n .catch(() => {\n restoreStdinAfterInk();\n resolve(null);\n });\n });\n}\n\nfunction getStatusLabel(type: string): string {\n switch (type) {\n case \"retracted\":\n return \"RETRACTED\";\n case \"concern\":\n return \"CONCERN\";\n case \"version_changed\":\n return \"VERSION\";\n case \"metadata_mismatch\":\n return \"MISMATCH\";\n case \"metadata_outdated\":\n return \"OUTDATED\";\n default:\n return \"WARNING\";\n }\n}\n\nfunction buildSelectOptions(finding: CheckFinding): SelectOption<FixActionType>[] {\n return getFixActionsForFinding(finding).map((a) => ({\n label: a.label,\n value: a.type,\n }));\n}\n\nasync function processFinding(\n result: FixInteractionResult,\n library: ILibrary,\n item: CslItem,\n resultId: string,\n finding: CheckFinding\n): Promise<void> {\n result.totalFindings++;\n const options = buildSelectOptions(finding);\n if (options.length === 0) return;\n\n const label = getStatusLabel(finding.type);\n const message = `[${label}] ${resultId}: ${finding.message}`;\n const selectedAction = await selectFixAction(message, options);\n\n if (selectedAction === null) {\n result.skipped++;\n return;\n }\n\n const actionResult = await applyFixAction(library, item, finding, selectedAction);\n\n if (!actionResult.applied) {\n process.stderr.write(` Error: ${actionResult.message}\\n`);\n return;\n }\n\n process.stderr.write(` ${actionResult.message}\\n`);\n if (selectedAction === \"skip\") {\n result.skipped++;\n } else {\n result.applied++;\n if (actionResult.removed) {\n result.removed.push(resultId);\n }\n }\n}\n\nexport async function runFixInteraction(\n results: CheckResult[],\n library: ILibrary,\n findItem: (id: string) => CslItem | undefined\n): Promise<FixInteractionResult> {\n const interactionResult: FixInteractionResult = {\n totalFindings: 0,\n applied: 0,\n skipped: 0,\n removed: [],\n };\n\n const warningResults = results.filter((r) => r.status === \"warning\");\n\n for (const checkResult of warningResults) {\n const item = findItem(checkResult.id);\n if (!item) continue;\n\n for (const finding of checkResult.findings) {\n await processFinding(interactionResult, library, item, checkResult.id, finding);\n }\n }\n\n return interactionResult;\n}\n"],"names":[],"mappings":"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"}
@@ -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-of6eJn8N.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.1";
22
+ const version$1 = "0.26.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-of6eJn8N.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-of6eJn8N.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-of6eJn8N.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-of6eJn8N.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-of6eJn8N.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-zafRwZEZ.js");
933
+ const { addAttachment: addAttachment2 } = await import("./index-k67fQbe4.js");
930
934
  return addAttachment2(this.library, options);
931
935
  }
932
936
  async attachList(options) {
933
- const { listAttachments: listAttachments2 } = await import("./index-zafRwZEZ.js");
937
+ const { listAttachments: listAttachments2 } = await import("./index-k67fQbe4.js");
934
938
  return listAttachments2(this.library, options);
935
939
  }
936
940
  async attachGet(options) {
937
- const { getAttachment: getAttachment2 } = await import("./index-zafRwZEZ.js");
941
+ const { getAttachment: getAttachment2 } = await import("./index-k67fQbe4.js");
938
942
  return getAttachment2(this.library, options);
939
943
  }
940
944
  async attachDetach(options) {
941
- const { detachAttachment: detachAttachment2 } = await import("./index-zafRwZEZ.js");
945
+ const { detachAttachment: detachAttachment2 } = await import("./index-k67fQbe4.js");
942
946
  return detachAttachment2(this.library, options);
943
947
  }
944
948
  async attachSync(options) {
945
- const { syncAttachments: syncAttachments2 } = await import("./index-zafRwZEZ.js");
949
+ const { syncAttachments: syncAttachments2 } = await import("./index-k67fQbe4.js");
946
950
  return syncAttachments2(this.library, options);
947
951
  }
948
952
  async attachOpen(options) {
949
- const { openAttachment: openAttachment2 } = await import("./index-zafRwZEZ.js");
953
+ const { openAttachment: openAttachment2 } = await import("./index-k67fQbe4.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-Bbl-roAS.js");
1809
+ const { selectReferencesOrExit } = await import("./reference-select-i1Cnmc16.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,259 @@ 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.save === false) {
2316
+ opOptions.save = false;
2317
+ }
2318
+ if (options.metadata === false) {
2319
+ opOptions.metadata = false;
2320
+ }
2321
+ if (appConfig) {
2322
+ const pubmed = {};
2323
+ if (appConfig.pubmed.email) pubmed.email = appConfig.pubmed.email;
2324
+ if (appConfig.pubmed.apiKey) pubmed.apiKey = appConfig.pubmed.apiKey;
2325
+ opOptions.config = {
2326
+ ...appConfig.email ? { email: appConfig.email } : {},
2327
+ pubmed
2328
+ };
2329
+ }
2330
+ return opOptions;
2331
+ }
2332
+ async function executeCheck(options, context, appConfig) {
2333
+ return context.library.check(buildCheckOptions(options, appConfig));
2334
+ }
2335
+ function getStatusLabel(result) {
2336
+ if (result.status === "skipped") return "[SKIPPED]";
2337
+ if (result.status === "ok") return "[OK]";
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) {
2356
+ case "retracted":
2357
+ return "[RETRACTED]";
2358
+ case "concern":
2359
+ return "[CONCERN]";
2360
+ case "version_changed":
2361
+ return "[VERSION]";
2362
+ case "metadata_mismatch":
2363
+ return "[MISMATCH]";
2364
+ case "metadata_outdated":
2365
+ return "[OUTDATED]";
2366
+ default:
2367
+ return "[WARNING]";
2368
+ }
2369
+ }
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) {
2376
+ const lines = [];
2377
+ if (finding.details?.fieldDiffs && finding.details.fieldDiffs.length > 0) {
2378
+ for (const diff of finding.details.fieldDiffs) {
2379
+ lines.push(formatFieldDiff(diff));
2380
+ }
2381
+ }
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
+ }
2394
+ }
2395
+ return lines;
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
+ }
2413
+ function formatCheckTextOutput(result) {
2414
+ const lines = [];
2415
+ for (const r of result.results) {
2416
+ const label = getStatusLabel(r);
2417
+ lines.push(`${label} ${r.id}`);
2418
+ for (const finding of r.findings) {
2419
+ lines.push(...formatFindingDetails(finding, r.id));
2420
+ }
2421
+ lines.push("");
2422
+ }
2423
+ const { summary } = result;
2424
+ const parts = [`${summary.total} checked`];
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
+ }
2441
+ if (summary.ok > 0) parts.push(`${summary.ok} ok`);
2442
+ if (summary.skipped > 0) parts.push(`${summary.skipped} skipped`);
2443
+ lines.push(`Summary: ${parts.join(", ")}`);
2444
+ return lines.join("\n");
2445
+ }
2446
+ function formatCheckJsonOutput(result, options) {
2447
+ if (!options?.full || !options.items) {
2448
+ return result;
2449
+ }
2450
+ const results = result.results.map((r) => {
2451
+ const item = options.items?.get(r.id);
2452
+ return item ? { ...r, item } : r;
2453
+ });
2454
+ return { ...result, results };
2455
+ }
2456
+ async function handleCheckAction(identifiers, options, globalOpts) {
2457
+ const outputFormat = options.output ?? "text";
2458
+ if (options.fix && !isTTY()) {
2459
+ outputCheckError(new Error("--fix requires an interactive terminal (TTY)"), outputFormat);
2460
+ setExitCode(ExitCode.ERROR);
2461
+ return;
2462
+ }
2463
+ try {
2464
+ const config2 = await loadConfigWithOverrides({ ...globalOpts, ...options });
2465
+ const context = await createExecutionContext(config2, Library.load);
2466
+ const ids = await resolveIdentifiers$1(identifiers, options, context, config2);
2467
+ if (ids === null) return;
2468
+ const result = await executeCheck({ ...options, identifiers: ids }, context, config2);
2469
+ const needAllRefs = options.full && outputFormat === "json" || options.fix;
2470
+ const allRefs = needAllRefs ? await context.library.getAll() : void 0;
2471
+ const jsonOptions = buildJsonOptionsFromRefs(options, outputFormat, result, allRefs);
2472
+ outputCheckResult(result, outputFormat, jsonOptions);
2473
+ if (options.fix && result.summary.warnings > 0 && allRefs) {
2474
+ const { runFixInteraction } = await import("./fix-interaction-DNXbmlPr.js");
2475
+ const findItem = (id2) => allRefs.find((item) => item.id === id2);
2476
+ const fixResult = await runFixInteraction(result.results, context.library, findItem);
2477
+ const removedSuffix = fixResult.removed.length > 0 ? `, ${fixResult.removed.length} removed` : "";
2478
+ process.stderr.write(
2479
+ `
2480
+ Fix summary: ${fixResult.applied} applied, ${fixResult.skipped} skipped${removedSuffix}
2481
+ `
2482
+ );
2483
+ }
2484
+ setExitCode(ExitCode.SUCCESS);
2485
+ } catch (error) {
2486
+ outputCheckError(error, outputFormat);
2487
+ setExitCode(ExitCode.ERROR);
2488
+ }
2489
+ }
2490
+ function buildJsonOptionsFromRefs(options, outputFormat, result, allRefs) {
2491
+ if (!options.full || outputFormat !== "json" || !allRefs) return void 0;
2492
+ const items2 = /* @__PURE__ */ new Map();
2493
+ for (const r of result.results) {
2494
+ const item = allRefs.find((ref2) => ref2.id === r.id);
2495
+ if (item) items2.set(r.id, item);
2496
+ }
2497
+ return { full: true, items: items2 };
2498
+ }
2499
+ async function resolveIdentifiers$1(identifiers, options, context, config2) {
2500
+ if (identifiers.length > 0 || options.all || options.search) {
2501
+ return identifiers;
2502
+ }
2503
+ if (isTTY()) {
2504
+ const ids = await selectReferencesInteractively(context, config2);
2505
+ if (ids.length === 0) {
2506
+ setExitCode(ExitCode.SUCCESS);
2507
+ return null;
2508
+ }
2509
+ return ids;
2510
+ }
2511
+ const stdinIds = await readIdentifiersFromStdin();
2512
+ if (stdinIds.length === 0) {
2513
+ process.stderr.write(
2514
+ "Error: No identifiers provided. Provide IDs, use --all, --search, or run interactively.\n"
2515
+ );
2516
+ setExitCode(ExitCode.ERROR);
2517
+ return null;
2518
+ }
2519
+ return stdinIds;
2520
+ }
2521
+ function outputCheckResult(result, format2, jsonOptions) {
2522
+ if (format2 === "json") {
2523
+ process.stdout.write(`${JSON.stringify(formatCheckJsonOutput(result, jsonOptions))}
2524
+ `);
2525
+ } else {
2526
+ process.stderr.write(`${formatCheckTextOutput(result)}
2527
+ `);
2528
+ }
2529
+ }
2530
+ function outputCheckError(error, format2) {
2531
+ const message = error instanceof Error ? error.message : String(error);
2532
+ if (format2 === "json") {
2533
+ process.stdout.write(`${JSON.stringify({ error: message })}
2534
+ `);
2535
+ } else {
2536
+ process.stderr.write(`Error: ${message}
2537
+ `);
2538
+ }
2539
+ }
2540
+ async function selectReferencesInteractively(context, config2) {
2541
+ const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
2542
+ const { selectReferenceItemsOrExit } = await import("./reference-select-i1Cnmc16.js");
2543
+ const allReferences = await context.library.getAll();
2544
+ if (allReferences.length === 0) {
2545
+ process.stderr.write("No references in library.\n");
2546
+ return [];
2547
+ }
2548
+ const selectedItems = await withAlternateScreen2(
2549
+ () => selectReferenceItemsOrExit(allReferences, { multiSelect: true }, config2.cli.tui)
2550
+ );
2551
+ return selectedItems.map((item) => item.id);
2552
+ }
2284
2553
  async function validateOptions$2(options) {
2285
2554
  if (options.output && !["text", "html", "rtf"].includes(options.output)) {
2286
2555
  throw new Error(`Invalid output format '${options.output}'. Must be one of: text, html, rtf`);
@@ -2338,8 +2607,8 @@ function getCiteExitCode(result) {
2338
2607
  }
2339
2608
  async function executeInteractiveCite(options, context, config2) {
2340
2609
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
2341
- const { runCiteFlow } = await import("./index-C36Us_zC.js");
2342
- const { buildStyleChoices, listCustomStyles } = await import("./style-select-BeAktUts.js");
2610
+ const { runCiteFlow } = await import("./index-tdmbNN9b.js");
2611
+ const { buildStyleChoices, listCustomStyles } = await import("./style-select-COnY01qb.js");
2343
2612
  const { search } = await import("./file-watcher-Dlx0PolG.js").then((n) => n.B);
2344
2613
  const { tokenize } = await import("./file-watcher-Dlx0PolG.js").then((n) => n.A);
2345
2614
  const { checkTTY } = await import("./tty-BMyaEOhX.js");
@@ -2419,6 +2688,7 @@ async function handleCiteAction(identifiers, options, globalOpts) {
2419
2688
  }
2420
2689
  const ENV_OVERRIDE_MAP = {
2421
2690
  REFERENCE_MANAGER_LIBRARY: "library",
2691
+ EMAIL: "email",
2422
2692
  REFERENCE_MANAGER_ATTACHMENTS_DIR: "attachments.directory",
2423
2693
  REFERENCE_MANAGER_CLI_DEFAULT_LIMIT: "cli.default_limit",
2424
2694
  REFERENCE_MANAGER_MCP_DEFAULT_LIMIT: "mcp.default_limit",
@@ -2454,6 +2724,7 @@ const CONFIG_KEY_REGISTRY = [
2454
2724
  description: "Log level",
2455
2725
  enumValues: ["silent", "info", "debug"]
2456
2726
  },
2727
+ { key: "email", type: "string", description: "Fallback email for API services", optional: true },
2457
2728
  // backup section
2458
2729
  { key: "backup.max_generations", type: "integer", description: "Maximum backup generations" },
2459
2730
  { key: "backup.max_age_days", type: "integer", description: "Maximum backup age in days" },
@@ -6938,7 +7209,7 @@ function formatEditOutput(result) {
6938
7209
  }
6939
7210
  async function executeInteractiveEdit(options, context, config2) {
6940
7211
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
6941
- const { selectReferencesOrExit } = await import("./reference-select-Bbl-roAS.js");
7212
+ const { selectReferencesOrExit } = await import("./reference-select-i1Cnmc16.js");
6942
7213
  const allReferences = await context.library.getAll();
6943
7214
  const identifiers = await withAlternateScreen2(
6944
7215
  () => selectReferencesOrExit(allReferences, { multiSelect: true }, config2.cli.tui)
@@ -10479,7 +10750,7 @@ function getFulltextExitCode(result) {
10479
10750
  }
10480
10751
  async function executeInteractiveSelect$1(context, config2) {
10481
10752
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
10482
- const { selectReferencesOrExit } = await import("./reference-select-Bbl-roAS.js");
10753
+ const { selectReferencesOrExit } = await import("./reference-select-i1Cnmc16.js");
10483
10754
  const allReferences = await context.library.getAll();
10484
10755
  const identifiers = await withAlternateScreen2(
10485
10756
  () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -31328,6 +31599,55 @@ function registerAddTool(server, getLibraryOperations) {
31328
31599
  }
31329
31600
  );
31330
31601
  }
31602
+ function formatCheckResult(result) {
31603
+ const lines = [];
31604
+ for (const r of result.results) {
31605
+ if (r.status === "ok") {
31606
+ lines.push(`[OK] ${r.id}`);
31607
+ } else if (r.status === "skipped") {
31608
+ lines.push(`[SKIPPED] ${r.id}`);
31609
+ } else {
31610
+ for (const f of r.findings) {
31611
+ lines.push(`[${f.type.toUpperCase()}] ${r.id}: ${f.message}`);
31612
+ }
31613
+ }
31614
+ }
31615
+ const { summary } = result;
31616
+ lines.push(
31617
+ `
31618
+ Summary: ${summary.total} checked, ${summary.ok} ok, ${summary.warnings} warnings, ${summary.skipped} skipped`
31619
+ );
31620
+ return lines.join("\n");
31621
+ }
31622
+ function registerCheckTool(server, getLibraryOperations) {
31623
+ server.registerTool(
31624
+ "check",
31625
+ {
31626
+ description: "Check references for retractions, expressions of concern, and version changes by querying Crossref and PubMed APIs.",
31627
+ inputSchema: {
31628
+ ids: z.array(z.string()).optional().describe("Array of reference IDs to check. Omit if using 'all'."),
31629
+ all: z.boolean().optional().describe("Check all references in library"),
31630
+ skipDays: z.number().optional().describe("Skip references checked within n days (default: 7)"),
31631
+ save: z.boolean().optional().describe("Whether to save results to library (default: true)"),
31632
+ metadata: z.boolean().optional().describe("Compare metadata against remote sources (default: true)")
31633
+ }
31634
+ },
31635
+ async (args) => {
31636
+ const libraryOps = getLibraryOperations();
31637
+ const options = {};
31638
+ if (args.ids?.length) options.identifiers = args.ids;
31639
+ if (args.all) options.all = true;
31640
+ if (args.skipDays !== void 0) options.skipDays = args.skipDays;
31641
+ if (args.save !== void 0) options.save = args.save;
31642
+ if (args.metadata !== void 0) options.metadata = args.metadata;
31643
+ const result = await libraryOps.check(options);
31644
+ const text = formatCheckResult(result);
31645
+ return {
31646
+ content: [{ type: "text", text }]
31647
+ };
31648
+ }
31649
+ );
31650
+ }
31331
31651
  function registerCiteTool(server, getLibraryOperations) {
31332
31652
  server.registerTool(
31333
31653
  "cite",
@@ -31718,6 +32038,7 @@ function registerAllTools(server, getLibraryOperations, getConfig) {
31718
32038
  registerSearchTool(server, getLibraryOperations, getConfig);
31719
32039
  registerListTool(server, getLibraryOperations, getConfig);
31720
32040
  registerCiteTool(server, getLibraryOperations);
32041
+ registerCheckTool(server, getLibraryOperations);
31721
32042
  registerAddTool(server, getLibraryOperations);
31722
32043
  registerRemoveTool(server, getLibraryOperations);
31723
32044
  registerFulltextAttachTool(server, getLibraryOperations, getConfig);
@@ -31779,7 +32100,7 @@ async function mcpStart(options) {
31779
32100
  async function executeRemove(options, context) {
31780
32101
  const { identifier, idType = "id", fulltextDirectory, deleteFulltext = false } = options;
31781
32102
  if (context.mode === "local" && deleteFulltext && fulltextDirectory) {
31782
- const { removeReference } = await import("./index-DWAtvFtp.js").then((n) => n.w);
32103
+ const { removeReference } = await import("./index-of6eJn8N.js").then((n) => n.x);
31783
32104
  return removeReference(context.library, {
31784
32105
  identifier,
31785
32106
  idType,
@@ -31834,7 +32155,7 @@ Continue?`;
31834
32155
  }
31835
32156
  async function executeInteractiveRemove(context, config2) {
31836
32157
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
31837
- const { selectReferenceItemsOrExit } = await import("./reference-select-Bbl-roAS.js");
32158
+ const { selectReferenceItemsOrExit } = await import("./reference-select-i1Cnmc16.js");
31838
32159
  const allReferences = await context.library.getAll();
31839
32160
  const selectedItems = await withAlternateScreen2(
31840
32161
  () => selectReferenceItemsOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -32059,7 +32380,7 @@ async function executeInteractiveSearch(options, context, config2) {
32059
32380
  validateInteractiveOptions(options);
32060
32381
  const { checkTTY } = await import("./tty-BMyaEOhX.js");
32061
32382
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
32062
- const { runSearchFlow } = await import("./index-C36Us_zC.js");
32383
+ const { runSearchFlow } = await import("./index-tdmbNN9b.js");
32063
32384
  const { search } = await import("./file-watcher-Dlx0PolG.js").then((n) => n.B);
32064
32385
  const { tokenize } = await import("./file-watcher-Dlx0PolG.js").then((n) => n.A);
32065
32386
  checkTTY();
@@ -32078,7 +32399,7 @@ async function executeInteractiveSearch(options, context, config2) {
32078
32399
  })
32079
32400
  );
32080
32401
  if (result.selectedItems && !result.cancelled) {
32081
- const { isSideEffectAction } = await import("./action-menu-BCgv1m3v.js");
32402
+ const { isSideEffectAction } = await import("./action-menu-DWdoHTFh.js");
32082
32403
  if (isSideEffectAction(result.action)) {
32083
32404
  await executeSideEffectAction(result.action, result.selectedItems, context, config2);
32084
32405
  return { output: "", cancelled: false, action: result.action };
@@ -32094,7 +32415,7 @@ async function executeSideEffectAction(action, items2, context, config2) {
32094
32415
  switch (action) {
32095
32416
  case "open-url": {
32096
32417
  const { resolveDefaultUrl: resolveDefaultUrl2 } = await Promise.resolve().then(() => url);
32097
- const { openWithSystemApp: openWithSystemApp2 } = await import("./loader-CV71qNY2.js").then((n) => n.j);
32418
+ const { openWithSystemApp: openWithSystemApp2 } = await import("./loader-B-fte1uv.js").then((n) => n.j);
32098
32419
  const item = items2[0];
32099
32420
  if (!item) return;
32100
32421
  const url$1 = resolveDefaultUrl2(item);
@@ -32481,7 +32802,7 @@ function formatUpdateOutput(result, identifier) {
32481
32802
  }
32482
32803
  async function executeInteractiveUpdate(context, config2) {
32483
32804
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
32484
- const { selectReferencesOrExit } = await import("./reference-select-Bbl-roAS.js");
32805
+ const { selectReferencesOrExit } = await import("./reference-select-i1Cnmc16.js");
32485
32806
  const allReferences = await context.library.getAll();
32486
32807
  const identifiers = await withAlternateScreen2(
32487
32808
  () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -32776,7 +33097,7 @@ function getUrlExitCode(result) {
32776
33097
  }
32777
33098
  async function executeInteractiveSelect(context, config2) {
32778
33099
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
32779
- const { selectReferencesOrExit } = await import("./reference-select-Bbl-roAS.js");
33100
+ const { selectReferencesOrExit } = await import("./reference-select-i1Cnmc16.js");
32780
33101
  const allReferences = await context.library.getAll();
32781
33102
  const identifiers = await withAlternateScreen2(
32782
33103
  () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -33147,6 +33468,7 @@ function createProgram() {
33147
33468
  registerRemoveCommand(program);
33148
33469
  registerUpdateCommand(program);
33149
33470
  registerEditCommand(program);
33471
+ registerCheckCommand(program);
33150
33472
  registerCiteCommand(program);
33151
33473
  registerServerCommand(program);
33152
33474
  registerFulltextCommand(program);
@@ -33308,7 +33630,7 @@ function shouldAutoFetch(cliFlag, configEnabled) {
33308
33630
  return configEnabled;
33309
33631
  }
33310
33632
  async function performAutoFetch(addedItems, context, config2) {
33311
- const { fulltextFetch: fulltextFetch2 } = await import("./index-DWAtvFtp.js").then((n) => n.v);
33633
+ const { fulltextFetch: fulltextFetch2 } = await import("./index-of6eJn8N.js").then((n) => n.w);
33312
33634
  const fetchResults = await autoFetchFulltext(addedItems, context, {
33313
33635
  fulltextConfig: config2.fulltext,
33314
33636
  fulltextDirectory: config2.attachments.directory,
@@ -33388,6 +33710,13 @@ function registerEditCommand(program) {
33388
33710
  await handleEditAction(identifiers, options, program.opts());
33389
33711
  });
33390
33712
  }
33713
+ function registerCheckCommand(program) {
33714
+ program.command("check").description(
33715
+ "Check references for retractions, expressions of concern, version changes, and metadata drift"
33716
+ ).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) => {
33717
+ await handleCheckAction(ids, options, program.opts());
33718
+ });
33719
+ }
33391
33720
  function registerCiteCommand(program) {
33392
33721
  program.command("cite").description("Generate formatted citations for references").argument(
33393
33722
  "[id-or-uuid...]",
@@ -33587,4 +33916,4 @@ export {
33587
33916
  restoreStdinAfterInk as r,
33588
33917
  syncAttachments as s
33589
33918
  };
33590
- //# sourceMappingURL=index-JA964gjc.js.map
33919
+ //# sourceMappingURL=index-BEQ4YIXx.js.map