@ncukondo/reference-manager 0.17.1 → 0.18.1

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 (58) hide show
  1. package/dist/chunks/action-menu-DRkjGlCc.js +87 -0
  2. package/dist/chunks/action-menu-DRkjGlCc.js.map +1 -0
  3. package/dist/chunks/clipboard-KXwFSJ4w.js +56 -0
  4. package/dist/chunks/clipboard-KXwFSJ4w.js.map +1 -0
  5. package/dist/chunks/{format-ByjxlVm5.js → format-DiPpWzBA.js} +2 -2
  6. package/dist/chunks/{format-ByjxlVm5.js.map → format-DiPpWzBA.js.map} +1 -1
  7. package/dist/chunks/{index-BigPRCTh.js → index-BB_a8Tm7.js} +50 -38
  8. package/dist/chunks/index-BB_a8Tm7.js.map +1 -0
  9. package/dist/chunks/{index-DWEWWvFO.js → index-GQ2KSWiQ.js} +2 -2
  10. package/dist/chunks/index-GQ2KSWiQ.js.map +1 -0
  11. package/dist/chunks/{index-wb8zgPJ0.js → index-vInTzmDN.js} +574 -92
  12. package/dist/chunks/index-vInTzmDN.js.map +1 -0
  13. package/dist/chunks/{loader-Ds4s8UO0.js → loader-BtW20O32.js} +40 -8
  14. package/dist/chunks/loader-BtW20O32.js.map +1 -0
  15. package/dist/chunks/{reference-select-O0PY7CRU.js → reference-select-DCXlkWpk.js} +3 -3
  16. package/dist/chunks/{reference-select-O0PY7CRU.js.map → reference-select-DCXlkWpk.js.map} +1 -1
  17. package/dist/chunks/{style-select-DF5kX4G6.js → style-select-BhBHyU3u.js} +2 -2
  18. package/dist/chunks/{style-select-DF5kX4G6.js.map → style-select-BhBHyU3u.js.map} +1 -1
  19. package/dist/cli/commands/attach.d.ts +4 -0
  20. package/dist/cli/commands/attach.d.ts.map +1 -1
  21. package/dist/cli/commands/cite.d.ts.map +1 -1
  22. package/dist/cli/commands/list.d.ts +6 -2
  23. package/dist/cli/commands/list.d.ts.map +1 -1
  24. package/dist/cli/commands/remove.d.ts +5 -0
  25. package/dist/cli/commands/remove.d.ts.map +1 -1
  26. package/dist/cli/commands/search.d.ts +18 -3
  27. package/dist/cli/commands/search.d.ts.map +1 -1
  28. package/dist/cli/commands/url.d.ts +58 -0
  29. package/dist/cli/commands/url.d.ts.map +1 -0
  30. package/dist/cli/helpers.d.ts +18 -0
  31. package/dist/cli/helpers.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/config/defaults.d.ts.map +1 -1
  35. package/dist/config/env-override.d.ts.map +1 -1
  36. package/dist/config/key-parser.d.ts.map +1 -1
  37. package/dist/config/loader.d.ts +7 -4
  38. package/dist/config/loader.d.ts.map +1 -1
  39. package/dist/config/schema.d.ts +27 -0
  40. package/dist/config/schema.d.ts.map +1 -1
  41. package/dist/features/format/items.d.ts +1 -1
  42. package/dist/features/format/items.d.ts.map +1 -1
  43. package/dist/features/interactive/action-menu.d.ts +40 -5
  44. package/dist/features/interactive/action-menu.d.ts.map +1 -1
  45. package/dist/features/interactive/apps/SearchFlowApp.d.ts +8 -3
  46. package/dist/features/interactive/apps/SearchFlowApp.d.ts.map +1 -1
  47. package/dist/features/interactive/apps/runSearchFlow.d.ts +5 -0
  48. package/dist/features/interactive/apps/runSearchFlow.d.ts.map +1 -1
  49. package/dist/features/operations/url.d.ts +24 -0
  50. package/dist/features/operations/url.d.ts.map +1 -0
  51. package/dist/index.js +1 -1
  52. package/dist/utils/clipboard.d.ts +19 -0
  53. package/dist/utils/clipboard.d.ts.map +1 -0
  54. package/package.json +2 -2
  55. package/dist/chunks/index-BigPRCTh.js.map +0 -1
  56. package/dist/chunks/index-DWEWWvFO.js.map +0 -1
  57. package/dist/chunks/index-wb8zgPJ0.js.map +0 -1
  58. package/dist/chunks/loader-Ds4s8UO0.js.map +0 -1
@@ -8,7 +8,7 @@ import * as path from "node:path";
8
8
  import path__default, { extname, join, basename, dirname } from "node:path";
9
9
  import fs__default, { stat, rename, copyFile, readFile, unlink, readdir, mkdir, rm } from "node:fs/promises";
10
10
  import { g as getExtension, i as isValidFulltextFiles, a as isReservedRole, F as FULLTEXT_ROLE, b as formatToExtension, c as findFulltextFiles, d as findFulltextFile, e as extensionToFormat, B as BUILTIN_STYLES, h as getFulltextAttachmentTypes, s as startServerWithFileWatcher } from "./index-B4gr0P83.js";
11
- import { o as openWithSystemApp, l as loadConfig, e as getDefaultCurrentDirConfigFilename, h as getDefaultUserConfigPath } from "./loader-Ds4s8UO0.js";
11
+ import { o as openWithSystemApp, l as loadConfig, e as getDefaultCurrentDirConfigFilename, h as getDefaultUserConfigPath } from "./loader-BtW20O32.js";
12
12
  import { spawn, spawnSync } from "node:child_process";
13
13
  import process$1, { stdin, stdout } from "node:process";
14
14
  import * as readline from "node:readline";
@@ -20,7 +20,7 @@ import "@citation-js/plugin-csl";
20
20
  import { ZodOptional as ZodOptional$2, z } from "zod";
21
21
  import { serve } from "@hono/node-server";
22
22
  const name = "@ncukondo/reference-manager";
23
- const version$1 = "0.17.1";
23
+ const version$1 = "0.18.1";
24
24
  const description$1 = "A local reference management tool using CSL-JSON as the single source of truth";
25
25
  const packageJson = {
26
26
  name,
@@ -1056,27 +1056,27 @@ class OperationsLibrary {
1056
1056
  }
1057
1057
  // Attachment operations
1058
1058
  async attachAdd(options) {
1059
- const { addAttachment: addAttachment2 } = await import("./index-DWEWWvFO.js");
1059
+ const { addAttachment: addAttachment2 } = await import("./index-GQ2KSWiQ.js");
1060
1060
  return addAttachment2(this.library, options);
1061
1061
  }
1062
1062
  async attachList(options) {
1063
- const { listAttachments: listAttachments2 } = await import("./index-DWEWWvFO.js");
1063
+ const { listAttachments: listAttachments2 } = await import("./index-GQ2KSWiQ.js");
1064
1064
  return listAttachments2(this.library, options);
1065
1065
  }
1066
1066
  async attachGet(options) {
1067
- const { getAttachment: getAttachment2 } = await import("./index-DWEWWvFO.js");
1067
+ const { getAttachment: getAttachment2 } = await import("./index-GQ2KSWiQ.js");
1068
1068
  return getAttachment2(this.library, options);
1069
1069
  }
1070
1070
  async attachDetach(options) {
1071
- const { detachAttachment: detachAttachment2 } = await import("./index-DWEWWvFO.js");
1071
+ const { detachAttachment: detachAttachment2 } = await import("./index-GQ2KSWiQ.js");
1072
1072
  return detachAttachment2(this.library, options);
1073
1073
  }
1074
1074
  async attachSync(options) {
1075
- const { syncAttachments: syncAttachments2 } = await import("./index-DWEWWvFO.js");
1075
+ const { syncAttachments: syncAttachments2 } = await import("./index-GQ2KSWiQ.js");
1076
1076
  return syncAttachments2(this.library, options);
1077
1077
  }
1078
1078
  async attachOpen(options) {
1079
- const { openAttachment: openAttachment2 } = await import("./index-DWEWWvFO.js");
1079
+ const { openAttachment: openAttachment2 } = await import("./index-GQ2KSWiQ.js");
1080
1080
  return openAttachment2(this.library, options);
1081
1081
  }
1082
1082
  }
@@ -1092,8 +1092,8 @@ class ServerClient {
1092
1092
  * @returns Array of CSL items
1093
1093
  */
1094
1094
  async getAll() {
1095
- const url = `${this.baseUrl}/api/references`;
1096
- const response = await fetch(url);
1095
+ const url2 = `${this.baseUrl}/api/references`;
1096
+ const response = await fetch(url2);
1097
1097
  if (!response.ok) {
1098
1098
  throw new Error(await response.text());
1099
1099
  }
@@ -1107,8 +1107,8 @@ class ServerClient {
1107
1107
  */
1108
1108
  async find(identifier, options = {}) {
1109
1109
  const { idType = "id" } = options;
1110
- const url = idType === "uuid" ? `${this.baseUrl}/api/references/uuid/${encodeURIComponent(identifier)}` : `${this.baseUrl}/api/references/id/${encodeURIComponent(identifier)}`;
1111
- const response = await fetch(url);
1110
+ const url2 = idType === "uuid" ? `${this.baseUrl}/api/references/uuid/${encodeURIComponent(identifier)}` : `${this.baseUrl}/api/references/id/${encodeURIComponent(identifier)}`;
1111
+ const response = await fetch(url2);
1112
1112
  if (response.status === 404) {
1113
1113
  return void 0;
1114
1114
  }
@@ -1126,8 +1126,8 @@ class ServerClient {
1126
1126
  * @returns The added CSL item (with generated ID and UUID)
1127
1127
  */
1128
1128
  async add(item) {
1129
- const url = `${this.baseUrl}/api/references`;
1130
- const response = await fetch(url, {
1129
+ const url2 = `${this.baseUrl}/api/references`;
1130
+ const response = await fetch(url2, {
1131
1131
  method: "POST",
1132
1132
  headers: { "Content-Type": "application/json" },
1133
1133
  body: JSON.stringify(item)
@@ -1146,8 +1146,8 @@ class ServerClient {
1146
1146
  */
1147
1147
  async update(identifier, updates, options) {
1148
1148
  const { idType = "id", onIdCollision } = options ?? {};
1149
- const url = idType === "uuid" ? `${this.baseUrl}/api/references/uuid/${encodeURIComponent(identifier)}` : `${this.baseUrl}/api/references/id/${encodeURIComponent(identifier)}`;
1150
- const response = await fetch(url, {
1149
+ const url2 = idType === "uuid" ? `${this.baseUrl}/api/references/uuid/${encodeURIComponent(identifier)}` : `${this.baseUrl}/api/references/id/${encodeURIComponent(identifier)}`;
1150
+ const response = await fetch(url2, {
1151
1151
  method: "PUT",
1152
1152
  headers: { "Content-Type": "application/json" },
1153
1153
  body: JSON.stringify({ updates, onIdCollision })
@@ -1171,8 +1171,8 @@ class ServerClient {
1171
1171
  */
1172
1172
  async remove(identifier, options = {}) {
1173
1173
  const { idType = "id" } = options;
1174
- const url = idType === "uuid" ? `${this.baseUrl}/api/references/uuid/${encodeURIComponent(identifier)}` : `${this.baseUrl}/api/references/id/${encodeURIComponent(identifier)}`;
1175
- const response = await fetch(url, {
1174
+ const url2 = idType === "uuid" ? `${this.baseUrl}/api/references/uuid/${encodeURIComponent(identifier)}` : `${this.baseUrl}/api/references/id/${encodeURIComponent(identifier)}`;
1175
+ const response = await fetch(url2, {
1176
1176
  method: "DELETE"
1177
1177
  });
1178
1178
  if (!response.ok && response.status !== 404) {
@@ -1203,8 +1203,8 @@ class ServerClient {
1203
1203
  * @returns Result containing added, failed, and skipped items
1204
1204
  */
1205
1205
  async import(inputs, options) {
1206
- const url = `${this.baseUrl}/api/add`;
1207
- const response = await fetch(url, {
1206
+ const url2 = `${this.baseUrl}/api/add`;
1207
+ const response = await fetch(url2, {
1208
1208
  method: "POST",
1209
1209
  headers: { "Content-Type": "application/json" },
1210
1210
  body: JSON.stringify({ inputs, options })
@@ -1220,8 +1220,8 @@ class ServerClient {
1220
1220
  * @returns Cite result with per-identifier results
1221
1221
  */
1222
1222
  async cite(options) {
1223
- const url = `${this.baseUrl}/api/cite`;
1224
- const response = await fetch(url, {
1223
+ const url2 = `${this.baseUrl}/api/cite`;
1224
+ const response = await fetch(url2, {
1225
1225
  method: "POST",
1226
1226
  headers: { "Content-Type": "application/json" },
1227
1227
  body: JSON.stringify(options)
@@ -1237,8 +1237,8 @@ class ServerClient {
1237
1237
  * @returns List result with raw CslItem[]
1238
1238
  */
1239
1239
  async list(options) {
1240
- const url = `${this.baseUrl}/api/list`;
1241
- const response = await fetch(url, {
1240
+ const url2 = `${this.baseUrl}/api/list`;
1241
+ const response = await fetch(url2, {
1242
1242
  method: "POST",
1243
1243
  headers: { "Content-Type": "application/json" },
1244
1244
  body: JSON.stringify(options ?? {})
@@ -1254,8 +1254,8 @@ class ServerClient {
1254
1254
  * @returns Search result with raw CslItem[]
1255
1255
  */
1256
1256
  async search(options) {
1257
- const url = `${this.baseUrl}/api/search`;
1258
- const response = await fetch(url, {
1257
+ const url2 = `${this.baseUrl}/api/search`;
1258
+ const response = await fetch(url2, {
1259
1259
  method: "POST",
1260
1260
  headers: { "Content-Type": "application/json" },
1261
1261
  body: JSON.stringify(options)
@@ -1274,8 +1274,8 @@ class ServerClient {
1274
1274
  * @returns Result of the add operation
1275
1275
  */
1276
1276
  async attachAdd(options) {
1277
- const url = `${this.baseUrl}/api/attachments/add`;
1278
- const response = await fetch(url, {
1277
+ const url2 = `${this.baseUrl}/api/attachments/add`;
1278
+ const response = await fetch(url2, {
1279
1279
  method: "POST",
1280
1280
  headers: { "Content-Type": "application/json" },
1281
1281
  body: JSON.stringify(options)
@@ -1291,8 +1291,8 @@ class ServerClient {
1291
1291
  * @returns List of attachments
1292
1292
  */
1293
1293
  async attachList(options) {
1294
- const url = `${this.baseUrl}/api/attachments/list`;
1295
- const response = await fetch(url, {
1294
+ const url2 = `${this.baseUrl}/api/attachments/list`;
1295
+ const response = await fetch(url2, {
1296
1296
  method: "POST",
1297
1297
  headers: { "Content-Type": "application/json" },
1298
1298
  body: JSON.stringify(options)
@@ -1308,8 +1308,8 @@ class ServerClient {
1308
1308
  * @returns Attachment file path or content
1309
1309
  */
1310
1310
  async attachGet(options) {
1311
- const url = `${this.baseUrl}/api/attachments/get`;
1312
- const response = await fetch(url, {
1311
+ const url2 = `${this.baseUrl}/api/attachments/get`;
1312
+ const response = await fetch(url2, {
1313
1313
  method: "POST",
1314
1314
  headers: { "Content-Type": "application/json" },
1315
1315
  body: JSON.stringify(options)
@@ -1325,8 +1325,8 @@ class ServerClient {
1325
1325
  * @returns Result of the detach operation
1326
1326
  */
1327
1327
  async attachDetach(options) {
1328
- const url = `${this.baseUrl}/api/attachments/detach`;
1329
- const response = await fetch(url, {
1328
+ const url2 = `${this.baseUrl}/api/attachments/detach`;
1329
+ const response = await fetch(url2, {
1330
1330
  method: "POST",
1331
1331
  headers: { "Content-Type": "application/json" },
1332
1332
  body: JSON.stringify(options)
@@ -1342,8 +1342,8 @@ class ServerClient {
1342
1342
  * @returns Sync result
1343
1343
  */
1344
1344
  async attachSync(options) {
1345
- const url = `${this.baseUrl}/api/attachments/sync`;
1346
- const response = await fetch(url, {
1345
+ const url2 = `${this.baseUrl}/api/attachments/sync`;
1346
+ const response = await fetch(url2, {
1347
1347
  method: "POST",
1348
1348
  headers: { "Content-Type": "application/json" },
1349
1349
  body: JSON.stringify(options)
@@ -1359,8 +1359,8 @@ class ServerClient {
1359
1359
  * @returns Result of the open operation
1360
1360
  */
1361
1361
  async attachOpen(options) {
1362
- const url = `${this.baseUrl}/api/attachments/open`;
1363
- const response = await fetch(url, {
1362
+ const url2 = `${this.baseUrl}/api/attachments/open`;
1363
+ const response = await fetch(url2, {
1364
1364
  method: "POST",
1365
1365
  headers: { "Content-Type": "application/json" },
1366
1366
  body: JSON.stringify(options)
@@ -1458,7 +1458,9 @@ function parseJsonInput(input) {
1458
1458
  }
1459
1459
  }
1460
1460
  async function loadConfigWithOverrides(options) {
1461
- const config2 = await loadConfig();
1461
+ const config2 = await loadConfig({
1462
+ ...options.config && { configPath: options.config }
1463
+ });
1462
1464
  const overrides = {};
1463
1465
  if (options.library) {
1464
1466
  overrides.library = options.library;
@@ -1566,6 +1568,35 @@ function exitWithError(message, code2 = ExitCode.ERROR) {
1566
1568
  `);
1567
1569
  setExitCode(code2);
1568
1570
  }
1571
+ function resolveClipboardEnabled(options, config2, isTui) {
1572
+ if (options.clipboard !== void 0) {
1573
+ return options.clipboard;
1574
+ }
1575
+ const envVal = process.env.REFERENCE_MANAGER_CLIPBOARD_AUTO_COPY;
1576
+ if (envVal !== void 0) {
1577
+ return envVal === "1" || envVal === "true";
1578
+ }
1579
+ if (isTui) {
1580
+ return config2.cli.tui.clipboardAutoCopy;
1581
+ }
1582
+ return false;
1583
+ }
1584
+ async function writeOutputWithClipboard(output, clipboardEnabled, quiet) {
1585
+ process.stdout.write(`${output}
1586
+ `);
1587
+ if (clipboardEnabled) {
1588
+ const { copyToClipboard } = await import("./clipboard-KXwFSJ4w.js");
1589
+ const result = await copyToClipboard(output);
1590
+ if (!quiet) {
1591
+ if (result.success) {
1592
+ process.stderr.write("Copied to clipboard\n");
1593
+ } else {
1594
+ process.stderr.write(`Warning: Failed to copy to clipboard: ${result.error}
1595
+ `);
1596
+ }
1597
+ }
1598
+ }
1599
+ }
1569
1600
  async function executeAttachOpen(options, context) {
1570
1601
  const operationOptions = {
1571
1602
  identifier: options.identifier,
@@ -1762,9 +1793,9 @@ function formatSyncPreview(result) {
1762
1793
  function getAttachExitCode(result) {
1763
1794
  return result.success ? 0 : 1;
1764
1795
  }
1765
- async function executeInteractiveSelect$1(context, config2) {
1796
+ async function executeInteractiveSelect$2(context, config2) {
1766
1797
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
1767
- const { selectReferencesOrExit } = await import("./reference-select-O0PY7CRU.js");
1798
+ const { selectReferencesOrExit } = await import("./reference-select-DCXlkWpk.js");
1768
1799
  const allReferences = await context.library.getAll();
1769
1800
  const identifiers = await withAlternateScreen2(
1770
1801
  () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -1776,7 +1807,7 @@ async function resolveIdentifier(identifierArg, context, config2) {
1776
1807
  return identifierArg;
1777
1808
  }
1778
1809
  if (isTTY()) {
1779
- return executeInteractiveSelect$1(context, config2);
1810
+ return executeInteractiveSelect$2(context, config2);
1780
1811
  }
1781
1812
  const stdinId = await readIdentifierFromStdin();
1782
1813
  if (!stdinId) {
@@ -1894,6 +1925,7 @@ async function handleAttachOpenAction(identifierArg, filenameArg, options, globa
1894
1925
  process.stdout.write(`${result.path}
1895
1926
  `);
1896
1927
  setExitCode(ExitCode.SUCCESS);
1928
+ return;
1897
1929
  }
1898
1930
  if (shouldUseInteractive) {
1899
1931
  await runInteractiveMode(
@@ -2080,6 +2112,28 @@ async function handleAttachSyncAction(identifierArg, options, globalOpts) {
2080
2112
  setExitCode(ExitCode.INTERNAL_ERROR);
2081
2113
  }
2082
2114
  }
2115
+ const attach = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2116
+ __proto__: null,
2117
+ executeAttachAdd,
2118
+ executeAttachDetach,
2119
+ executeAttachGet,
2120
+ executeAttachList,
2121
+ executeAttachOpen,
2122
+ executeAttachSync,
2123
+ formatAttachAddOutput,
2124
+ formatAttachDetachOutput,
2125
+ formatAttachListOutput,
2126
+ formatAttachOpenOutput,
2127
+ formatAttachSyncOutput,
2128
+ getAttachExitCode,
2129
+ handleAttachAddAction,
2130
+ handleAttachDetachAction,
2131
+ handleAttachGetAction,
2132
+ handleAttachListAction,
2133
+ handleAttachOpenAction,
2134
+ handleAttachSyncAction,
2135
+ runInteractiveMode
2136
+ }, Symbol.toStringTag, { value: "Module" }));
2083
2137
  async function validateOptions$2(options) {
2084
2138
  if (options.output && !["text", "html", "rtf"].includes(options.output)) {
2085
2139
  throw new Error(`Invalid output format '${options.output}'. Must be one of: text, html, rtf`);
@@ -2137,8 +2191,8 @@ function getCiteExitCode(result) {
2137
2191
  }
2138
2192
  async function executeInteractiveCite(options, context, config2) {
2139
2193
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
2140
- const { runCiteFlow } = await import("./index-BigPRCTh.js");
2141
- const { buildStyleChoices, listCustomStyles } = await import("./style-select-DF5kX4G6.js");
2194
+ const { runCiteFlow } = await import("./index-BB_a8Tm7.js");
2195
+ const { buildStyleChoices, listCustomStyles } = await import("./style-select-BhBHyU3u.js");
2142
2196
  const { search } = await import("./file-watcher-CrsNHUpz.js").then((n) => n.z);
2143
2197
  const { tokenize } = await import("./file-watcher-CrsNHUpz.js").then((n) => n.y);
2144
2198
  const { checkTTY } = await import("./tty-BMyaEOhX.js");
@@ -2180,8 +2234,10 @@ async function handleCiteAction(identifiers, options, globalOpts) {
2180
2234
  const config2 = await loadConfigWithOverrides({ ...globalOpts, ...options });
2181
2235
  const context = await createExecutionContext(config2, Library.load);
2182
2236
  let result;
2237
+ let isTuiMode = false;
2183
2238
  if (identifiers.length === 0) {
2184
2239
  if (isTTY()) {
2240
+ isTuiMode = true;
2185
2241
  result = await executeInteractiveCite(options, context, config2);
2186
2242
  } else {
2187
2243
  const stdinIds = await readIdentifiersFromStdin();
@@ -2199,8 +2255,8 @@ async function handleCiteAction(identifiers, options, globalOpts) {
2199
2255
  }
2200
2256
  const output = formatCiteOutput(result);
2201
2257
  if (output) {
2202
- process.stdout.write(`${output}
2203
- `);
2258
+ const clipboardEnabled = resolveClipboardEnabled(globalOpts, config2, isTuiMode);
2259
+ await writeOutputWithClipboard(output, clipboardEnabled, config2.logLevel === "silent");
2204
2260
  }
2205
2261
  const errors2 = formatCiteErrors(result);
2206
2262
  if (errors2) {
@@ -2219,6 +2275,7 @@ const ENV_OVERRIDE_MAP = {
2219
2275
  REFERENCE_MANAGER_ATTACHMENTS_DIR: "attachments.directory",
2220
2276
  REFERENCE_MANAGER_CLI_DEFAULT_LIMIT: "cli.default_limit",
2221
2277
  REFERENCE_MANAGER_MCP_DEFAULT_LIMIT: "mcp.default_limit",
2278
+ REFERENCE_MANAGER_CLIPBOARD_AUTO_COPY: "cli.tui.clipboard_auto_copy",
2222
2279
  PUBMED_EMAIL: "pubmed.email",
2223
2280
  PUBMED_API_KEY: "pubmed.api_key"
2224
2281
  };
@@ -2275,6 +2332,12 @@ const CONFIG_KEY_REGISTRY = [
2275
2332
  description: "Default format",
2276
2333
  enumValues: ["text", "html", "rtf"]
2277
2334
  },
2335
+ {
2336
+ key: "citation.default_key_format",
2337
+ type: "enum",
2338
+ description: "Default citation key format",
2339
+ enumValues: ["pandoc", "latex"]
2340
+ },
2278
2341
  // pubmed section
2279
2342
  { key: "pubmed.email", type: "string", description: "Email for PubMed API", optional: true },
2280
2343
  { key: "pubmed.api_key", type: "string", description: "API key for PubMed", optional: true },
@@ -2309,6 +2372,11 @@ const CONFIG_KEY_REGISTRY = [
2309
2372
  type: "integer",
2310
2373
  description: "Search debounce delay (ms)"
2311
2374
  },
2375
+ {
2376
+ key: "cli.tui.clipboard_auto_copy",
2377
+ type: "boolean",
2378
+ description: "Auto-copy TUI output to clipboard"
2379
+ },
2312
2380
  // cli.edit section
2313
2381
  {
2314
2382
  key: "cli.edit.default_format",
@@ -6681,7 +6749,7 @@ function formatEditOutput(result) {
6681
6749
  }
6682
6750
  async function executeInteractiveEdit(options, context, config2) {
6683
6751
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
6684
- const { selectReferencesOrExit } = await import("./reference-select-O0PY7CRU.js");
6752
+ const { selectReferencesOrExit } = await import("./reference-select-DCXlkWpk.js");
6685
6753
  const allReferences = await context.library.getAll();
6686
6754
  const identifiers = await withAlternateScreen2(
6687
6755
  () => selectReferencesOrExit(allReferences, { multiSelect: true }, config2.cli.tui)
@@ -6747,6 +6815,12 @@ async function handleEditAction(identifiers, options, globalOpts) {
6747
6815
  setExitCode(ExitCode.INTERNAL_ERROR);
6748
6816
  }
6749
6817
  }
6818
+ const edit = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
6819
+ __proto__: null,
6820
+ executeEditCommand,
6821
+ formatEditOutput,
6822
+ handleEditAction
6823
+ }, Symbol.toStringTag, { value: "Module" }));
6750
6824
  const ALIAS = Symbol.for("yaml.alias");
6751
6825
  const DOC = Symbol.for("yaml.document");
6752
6826
  const MAP = Symbol.for("yaml.map");
@@ -10327,9 +10401,9 @@ function formatFulltextOpenOutput(result) {
10327
10401
  function getFulltextExitCode(result) {
10328
10402
  return result.success ? 0 : 1;
10329
10403
  }
10330
- async function executeInteractiveSelect(context, config2) {
10404
+ async function executeInteractiveSelect$1(context, config2) {
10331
10405
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
10332
- const { selectReferencesOrExit } = await import("./reference-select-O0PY7CRU.js");
10406
+ const { selectReferencesOrExit } = await import("./reference-select-DCXlkWpk.js");
10333
10407
  const allReferences = await context.library.getAll();
10334
10408
  const identifiers = await withAlternateScreen2(
10335
10409
  () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -10366,7 +10440,7 @@ async function handleFulltextAttachAction(identifierArg, filePathArg, options, g
10366
10440
  setExitCode(ExitCode.ERROR);
10367
10441
  return;
10368
10442
  }
10369
- identifier = await executeInteractiveSelect(context, config2);
10443
+ identifier = await executeInteractiveSelect$1(context, config2);
10370
10444
  }
10371
10445
  const { type: type2, filePath } = parseFulltextAttachTypeAndPath(filePathArg, options);
10372
10446
  const stdinContent = !filePath && type2 ? await readStdinBuffer() : void 0;
@@ -10413,7 +10487,7 @@ async function handleFulltextGetAction(identifierArg, options, globalOpts) {
10413
10487
  if (identifierArg) {
10414
10488
  identifier = identifierArg;
10415
10489
  } else if (isTTY()) {
10416
- identifier = await executeInteractiveSelect(context, config2);
10490
+ identifier = await executeInteractiveSelect$1(context, config2);
10417
10491
  } else {
10418
10492
  const stdinId = await readIdentifierFromStdin();
10419
10493
  if (!stdinId) {
@@ -10450,7 +10524,7 @@ async function handleFulltextDetachAction(identifierArg, options, globalOpts) {
10450
10524
  if (identifierArg) {
10451
10525
  identifier = identifierArg;
10452
10526
  } else if (isTTY()) {
10453
- identifier = await executeInteractiveSelect(context, config2);
10527
+ identifier = await executeInteractiveSelect$1(context, config2);
10454
10528
  } else {
10455
10529
  const stdinId = await readIdentifierFromStdin();
10456
10530
  if (!stdinId) {
@@ -10490,7 +10564,7 @@ async function handleFulltextOpenAction(identifierArg, options, globalOpts) {
10490
10564
  if (identifierArg) {
10491
10565
  identifier = identifierArg;
10492
10566
  } else if (isTTY()) {
10493
- identifier = await executeInteractiveSelect(context, config2);
10567
+ identifier = await executeInteractiveSelect$1(context, config2);
10494
10568
  } else {
10495
10569
  const stdinId = await readIdentifierFromStdin();
10496
10570
  if (!stdinId) {
@@ -10520,6 +10594,22 @@ async function handleFulltextOpenAction(identifierArg, options, globalOpts) {
10520
10594
  setExitCode(ExitCode.INTERNAL_ERROR);
10521
10595
  }
10522
10596
  }
10597
+ const fulltext = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
10598
+ __proto__: null,
10599
+ executeFulltextAttach,
10600
+ executeFulltextDetach,
10601
+ executeFulltextGet,
10602
+ executeFulltextOpen,
10603
+ formatFulltextAttachOutput,
10604
+ formatFulltextDetachOutput,
10605
+ formatFulltextGetOutput,
10606
+ formatFulltextOpenOutput,
10607
+ getFulltextExitCode,
10608
+ handleFulltextAttachAction,
10609
+ handleFulltextDetachAction,
10610
+ handleFulltextGetAction,
10611
+ handleFulltextOpenAction
10612
+ }, Symbol.toStringTag, { value: "Module" }));
10523
10613
  function formatAuthor(author) {
10524
10614
  const family = author.family || "";
10525
10615
  const givenInitial = author.given ? `${author.given.charAt(0)}.` : "";
@@ -10572,6 +10662,10 @@ function formatItems(items2, format2) {
10572
10662
  return items2.filter(
10573
10663
  (item) => Boolean(item.custom?.uuid)
10574
10664
  ).map((item) => item.custom.uuid);
10665
+ case "pandoc-key":
10666
+ return items2.map((item) => `@${item.id}`);
10667
+ case "latex-key":
10668
+ return items2.map((item) => `\\cite{${item.id}}`);
10575
10669
  default:
10576
10670
  return items2.map((item) => formatPretty([item]));
10577
10671
  }
@@ -10610,11 +10704,14 @@ const VALID_LIST_SORT_FIELDS = /* @__PURE__ */ new Set([
10610
10704
  "mod",
10611
10705
  "pub"
10612
10706
  ]);
10613
- function getOutputFormat$1(options) {
10707
+ function getOutputFormat$1(options, defaultKeyFormat) {
10614
10708
  if (options.output) {
10615
10709
  if (options.output === "ids") return "ids-only";
10616
10710
  return options.output;
10617
10711
  }
10712
+ if (options.key) return defaultKeyFormat === "latex" ? "latex-key" : "pandoc-key";
10713
+ if (options.pandocKey) return "pandoc-key";
10714
+ if (options.latexKey) return "latex-key";
10618
10715
  if (options.json) return "json";
10619
10716
  if (options.idsOnly) return "ids-only";
10620
10717
  if (options.uuidOnly) return "uuid";
@@ -10622,12 +10719,18 @@ function getOutputFormat$1(options) {
10622
10719
  return "pretty";
10623
10720
  }
10624
10721
  function validateOptions$1(options) {
10625
- const outputOptions = [options.json, options.idsOnly, options.uuidOnly, options.bibtex].filter(
10626
- Boolean
10627
- );
10722
+ const outputOptions = [
10723
+ options.json,
10724
+ options.idsOnly,
10725
+ options.uuidOnly,
10726
+ options.bibtex,
10727
+ options.key,
10728
+ options.pandocKey,
10729
+ options.latexKey
10730
+ ].filter(Boolean);
10628
10731
  if (outputOptions.length > 1) {
10629
10732
  throw new Error(
10630
- "Multiple output formats specified. Only one of --json, --ids-only, --uuid-only, --bibtex can be used."
10733
+ "Multiple output formats specified. Only one of --json, --ids-only, --uuid-only, --bibtex, --key, --pandoc-key, --latex-key can be used."
10631
10734
  );
10632
10735
  }
10633
10736
  if (options.output && outputOptions.length > 0) {
@@ -10662,8 +10765,8 @@ async function executeList(options, context) {
10662
10765
  ...pickDefined(options, ["order", "limit", "offset"])
10663
10766
  });
10664
10767
  }
10665
- function formatListOutput(result, options) {
10666
- const format2 = getOutputFormat$1(options);
10768
+ function formatListOutput(result, options, defaultKeyFormat) {
10769
+ const format2 = getOutputFormat$1(options, defaultKeyFormat);
10667
10770
  if (format2 === "json") {
10668
10771
  return JSON.stringify({
10669
10772
  items: result.items,
@@ -15480,10 +15583,10 @@ const $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
15480
15583
  inst._zod.check = (payload) => {
15481
15584
  try {
15482
15585
  const trimmed = payload.value.trim();
15483
- const url = new URL(trimmed);
15586
+ const url2 = new URL(trimmed);
15484
15587
  if (def.hostname) {
15485
15588
  def.hostname.lastIndex = 0;
15486
- if (!def.hostname.test(url.hostname)) {
15589
+ if (!def.hostname.test(url2.hostname)) {
15487
15590
  payload.issues.push({
15488
15591
  code: "invalid_format",
15489
15592
  format: "url",
@@ -15497,7 +15600,7 @@ const $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
15497
15600
  }
15498
15601
  if (def.protocol) {
15499
15602
  def.protocol.lastIndex = 0;
15500
- if (!def.protocol.test(url.protocol.endsWith(":") ? url.protocol.slice(0, -1) : url.protocol)) {
15603
+ if (!def.protocol.test(url2.protocol.endsWith(":") ? url2.protocol.slice(0, -1) : url2.protocol)) {
15501
15604
  payload.issues.push({
15502
15605
  code: "invalid_format",
15503
15606
  format: "url",
@@ -15510,7 +15613,7 @@ const $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
15510
15613
  }
15511
15614
  }
15512
15615
  if (def.normalize) {
15513
- payload.value = url.href;
15616
+ payload.value = url2.href;
15514
15617
  } else {
15515
15618
  payload.value = trimmed;
15516
15619
  }
@@ -31388,7 +31491,7 @@ Continue?`;
31388
31491
  }
31389
31492
  async function executeInteractiveRemove(context, config2) {
31390
31493
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
31391
- const { selectReferenceItemsOrExit } = await import("./reference-select-O0PY7CRU.js");
31494
+ const { selectReferenceItemsOrExit } = await import("./reference-select-DCXlkWpk.js");
31392
31495
  const allReferences = await context.library.getAll();
31393
31496
  const selectedItems = await withAlternateScreen2(
31394
31497
  () => selectReferenceItemsOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -31485,6 +31588,15 @@ async function handleRemoveAction(identifierArg, options, globalOpts) {
31485
31588
  handleRemoveError(error, identifierArg, outputFormat);
31486
31589
  }
31487
31590
  }
31591
+ const remove = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
31592
+ __proto__: null,
31593
+ confirmRemoveIfNeeded,
31594
+ executeRemove,
31595
+ formatFulltextWarning,
31596
+ formatRemoveOutput,
31597
+ getFulltextAttachmentTypes,
31598
+ handleRemoveAction
31599
+ }, Symbol.toStringTag, { value: "Module" }));
31488
31600
  const VALID_SEARCH_SORT_FIELDS = /* @__PURE__ */ new Set([
31489
31601
  "created",
31490
31602
  "updated",
@@ -31497,11 +31609,14 @@ const VALID_SEARCH_SORT_FIELDS = /* @__PURE__ */ new Set([
31497
31609
  "pub",
31498
31610
  "rel"
31499
31611
  ]);
31500
- function getOutputFormat(options) {
31612
+ function getOutputFormat(options, defaultKeyFormat) {
31501
31613
  if (options.output) {
31502
31614
  if (options.output === "ids") return "ids-only";
31503
31615
  return options.output;
31504
31616
  }
31617
+ if (options.key) return defaultKeyFormat === "latex" ? "latex-key" : "pandoc-key";
31618
+ if (options.pandocKey) return "pandoc-key";
31619
+ if (options.latexKey) return "latex-key";
31505
31620
  if (options.json) return "json";
31506
31621
  if (options.idsOnly) return "ids-only";
31507
31622
  if (options.uuidOnly) return "uuid";
@@ -31509,12 +31624,18 @@ function getOutputFormat(options) {
31509
31624
  return "pretty";
31510
31625
  }
31511
31626
  function validateOptions(options) {
31512
- const outputOptions = [options.json, options.idsOnly, options.uuidOnly, options.bibtex].filter(
31513
- Boolean
31514
- );
31627
+ const outputOptions = [
31628
+ options.json,
31629
+ options.idsOnly,
31630
+ options.uuidOnly,
31631
+ options.bibtex,
31632
+ options.key,
31633
+ options.pandocKey,
31634
+ options.latexKey
31635
+ ].filter(Boolean);
31515
31636
  if (outputOptions.length > 1) {
31516
31637
  throw new Error(
31517
- "Multiple output formats specified. Only one of --json, --ids-only, --uuid-only, --bibtex can be used."
31638
+ "Multiple output formats specified. Only one of --json, --ids-only, --uuid-only, --bibtex, --key, --pandoc-key, --latex-key can be used."
31518
31639
  );
31519
31640
  }
31520
31641
  if (options.output && outputOptions.length > 0) {
@@ -31550,8 +31671,8 @@ async function executeSearch(options, context) {
31550
31671
  ...pickDefined(options, ["order", "limit", "offset"])
31551
31672
  });
31552
31673
  }
31553
- function formatSearchOutput(result, options) {
31554
- const format2 = getOutputFormat(options);
31674
+ function formatSearchOutput(result, options, defaultKeyFormat) {
31675
+ const format2 = getOutputFormat(options, defaultKeyFormat);
31555
31676
  if (format2 === "json") {
31556
31677
  return JSON.stringify({
31557
31678
  items: result.items,
@@ -31580,11 +31701,14 @@ function validateInteractiveOptions(options) {
31580
31701
  options.json,
31581
31702
  options.idsOnly,
31582
31703
  options.uuidOnly,
31583
- options.bibtex
31704
+ options.bibtex,
31705
+ options.key,
31706
+ options.pandocKey,
31707
+ options.latexKey
31584
31708
  ].filter(Boolean);
31585
31709
  if (outputOptions.length > 0) {
31586
31710
  throw new Error(
31587
- "TUI mode cannot be combined with output format options (--output, --json, --ids-only, --uuid-only, --bibtex)"
31711
+ "TUI mode cannot be combined with output format options (--output, --json, --ids-only, --uuid-only, --bibtex, --key, --pandoc-key, --latex-key)"
31588
31712
  );
31589
31713
  }
31590
31714
  }
@@ -31592,7 +31716,7 @@ async function executeInteractiveSearch(options, context, config2) {
31592
31716
  validateInteractiveOptions(options);
31593
31717
  const { checkTTY } = await import("./tty-BMyaEOhX.js");
31594
31718
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
31595
- const { runSearchFlow } = await import("./index-BigPRCTh.js");
31719
+ const { runSearchFlow } = await import("./index-BB_a8Tm7.js");
31596
31720
  const { search } = await import("./file-watcher-CrsNHUpz.js").then((n) => n.z);
31597
31721
  const { tokenize } = await import("./file-watcher-CrsNHUpz.js").then((n) => n.y);
31598
31722
  checkTTY();
@@ -31605,14 +31729,122 @@ async function executeInteractiveSearch(options, context, config2) {
31605
31729
  const result = await withAlternateScreen2(
31606
31730
  () => runSearchFlow(allReferences, searchFn, {
31607
31731
  limit: tuiConfig.limit,
31608
- debounceMs: tuiConfig.debounceMs
31732
+ debounceMs: tuiConfig.debounceMs,
31733
+ defaultKeyFormat: config2.citation.defaultKeyFormat,
31734
+ defaultStyle: config2.citation.defaultStyle
31609
31735
  })
31610
31736
  );
31737
+ if (result.selectedItems && !result.cancelled) {
31738
+ const { isSideEffectAction } = await import("./action-menu-DRkjGlCc.js");
31739
+ if (isSideEffectAction(result.action)) {
31740
+ await executeSideEffectAction(result.action, result.selectedItems, context, config2);
31741
+ return { output: "", cancelled: false, action: result.action };
31742
+ }
31743
+ }
31611
31744
  return {
31612
31745
  output: result.output,
31613
- cancelled: result.cancelled
31746
+ cancelled: result.cancelled,
31747
+ action: result.action
31614
31748
  };
31615
31749
  }
31750
+ async function executeSideEffectAction(action, items2, context, config2) {
31751
+ switch (action) {
31752
+ case "open-url": {
31753
+ const { resolveDefaultUrl: resolveDefaultUrl2 } = await Promise.resolve().then(() => url);
31754
+ const { openWithSystemApp: openWithSystemApp2 } = await import("./loader-BtW20O32.js").then((n) => n.j);
31755
+ const item = items2[0];
31756
+ if (!item) return;
31757
+ const url$1 = resolveDefaultUrl2(item);
31758
+ if (url$1) {
31759
+ await openWithSystemApp2(url$1);
31760
+ } else {
31761
+ process.stderr.write(`No URL available for ${item.id}
31762
+ `);
31763
+ }
31764
+ break;
31765
+ }
31766
+ case "open-fulltext": {
31767
+ const { executeFulltextOpen: executeFulltextOpen2 } = await Promise.resolve().then(() => fulltext);
31768
+ const item = items2[0];
31769
+ if (!item) return;
31770
+ const result = await executeFulltextOpen2(
31771
+ {
31772
+ identifier: item.id,
31773
+ fulltextDirectory: config2.attachments.directory
31774
+ },
31775
+ context
31776
+ );
31777
+ if (!result.success) {
31778
+ process.stderr.write(`${result.error}
31779
+ `);
31780
+ }
31781
+ break;
31782
+ }
31783
+ case "manage-attachments": {
31784
+ const { executeAttachOpen: executeAttachOpen2, runInteractiveMode: runInteractiveMode2 } = await Promise.resolve().then(() => attach);
31785
+ const item = items2[0];
31786
+ if (!item) return;
31787
+ const result = await executeAttachOpen2(
31788
+ {
31789
+ identifier: item.id,
31790
+ attachmentsDirectory: config2.attachments.directory
31791
+ },
31792
+ context
31793
+ );
31794
+ if (!result.success) {
31795
+ process.stderr.write(`Error: ${result.error}
31796
+ `);
31797
+ return;
31798
+ }
31799
+ await runInteractiveMode2(
31800
+ item.id,
31801
+ result.path ?? "",
31802
+ config2.attachments.directory,
31803
+ void 0,
31804
+ context
31805
+ );
31806
+ break;
31807
+ }
31808
+ case "edit": {
31809
+ const { executeEditCommand: executeEditCommand2 } = await Promise.resolve().then(() => edit);
31810
+ await executeEditCommand2(
31811
+ {
31812
+ identifiers: items2.map((i) => i.id),
31813
+ format: config2.cli.edit.defaultFormat
31814
+ },
31815
+ context
31816
+ );
31817
+ break;
31818
+ }
31819
+ case "remove": {
31820
+ const {
31821
+ executeRemove: executeRemove2,
31822
+ confirmRemoveIfNeeded: confirmRemoveIfNeeded2,
31823
+ getFulltextAttachmentTypes: getFulltextAttachmentTypes2,
31824
+ formatRemoveOutput: formatRemoveOutput2
31825
+ } = await Promise.resolve().then(() => remove);
31826
+ for (const item of items2) {
31827
+ const hasFulltext = getFulltextAttachmentTypes2(item).length > 0;
31828
+ const confirmed = await confirmRemoveIfNeeded2(item, hasFulltext, false);
31829
+ if (!confirmed) {
31830
+ process.stderr.write("Cancelled.\n");
31831
+ continue;
31832
+ }
31833
+ const result = await executeRemove2(
31834
+ {
31835
+ identifier: item.id,
31836
+ fulltextDirectory: config2.attachments.directory,
31837
+ deleteFulltext: hasFulltext
31838
+ },
31839
+ context
31840
+ );
31841
+ process.stderr.write(`${formatRemoveOutput2(result, item.id)}
31842
+ `);
31843
+ }
31844
+ break;
31845
+ }
31846
+ }
31847
+ }
31616
31848
  async function serverStart(options) {
31617
31849
  const existingStatus = await serverStatus(options.portfilePath);
31618
31850
  if (existingStatus !== null) {
@@ -31906,7 +32138,7 @@ function formatUpdateOutput(result, identifier) {
31906
32138
  }
31907
32139
  async function executeInteractiveUpdate(context, config2) {
31908
32140
  const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
31909
- const { selectReferencesOrExit } = await import("./reference-select-O0PY7CRU.js");
32141
+ const { selectReferencesOrExit } = await import("./reference-select-DCXlkWpk.js");
31910
32142
  const allReferences = await context.library.getAll();
31911
32143
  const identifiers = await withAlternateScreen2(
31912
32144
  () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
@@ -32021,6 +32253,231 @@ async function handleUpdateAction(identifierArg, file, options, globalOpts) {
32021
32253
  function collectSetOption(value, previous) {
32022
32254
  return previous.concat([value]);
32023
32255
  }
32256
+ function buildDoiUrl(doi) {
32257
+ return `https://doi.org/${doi}`;
32258
+ }
32259
+ function buildPubmedUrl(pmid) {
32260
+ return `https://pubmed.ncbi.nlm.nih.gov/${pmid}/`;
32261
+ }
32262
+ function buildPmcUrl(pmcid) {
32263
+ return `https://www.ncbi.nlm.nih.gov/pmc/articles/${pmcid}/`;
32264
+ }
32265
+ function resolveAllUrls(item) {
32266
+ const urls = [];
32267
+ if (item.DOI) {
32268
+ urls.push(buildDoiUrl(item.DOI));
32269
+ }
32270
+ if (item.URL) {
32271
+ urls.push(item.URL);
32272
+ }
32273
+ if (item.PMID) {
32274
+ urls.push(buildPubmedUrl(item.PMID));
32275
+ }
32276
+ if (item.PMCID) {
32277
+ urls.push(buildPmcUrl(item.PMCID));
32278
+ }
32279
+ const additionalUrls = item.custom?.additional_urls;
32280
+ if (additionalUrls && additionalUrls.length > 0) {
32281
+ urls.push(...additionalUrls);
32282
+ }
32283
+ return urls;
32284
+ }
32285
+ function resolveDefaultUrl(item) {
32286
+ if (item.DOI) {
32287
+ return buildDoiUrl(item.DOI);
32288
+ }
32289
+ if (item.URL) {
32290
+ return item.URL;
32291
+ }
32292
+ if (item.PMID) {
32293
+ return buildPubmedUrl(item.PMID);
32294
+ }
32295
+ if (item.PMCID) {
32296
+ return buildPmcUrl(item.PMCID);
32297
+ }
32298
+ const additionalUrls = item.custom?.additional_urls;
32299
+ if (additionalUrls && additionalUrls.length > 0) {
32300
+ return additionalUrls[0] ?? null;
32301
+ }
32302
+ return null;
32303
+ }
32304
+ function resolveUrlByType(item, type2) {
32305
+ switch (type2) {
32306
+ case "doi":
32307
+ return item.DOI ? buildDoiUrl(item.DOI) : null;
32308
+ case "url":
32309
+ return item.URL ?? null;
32310
+ case "pubmed":
32311
+ return item.PMID ? buildPubmedUrl(item.PMID) : null;
32312
+ case "pmcid":
32313
+ return item.PMCID ? buildPmcUrl(item.PMCID) : null;
32314
+ }
32315
+ }
32316
+ const url = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
32317
+ __proto__: null,
32318
+ resolveAllUrls,
32319
+ resolveDefaultUrl,
32320
+ resolveUrlByType
32321
+ }, Symbol.toStringTag, { value: "Module" }));
32322
+ function getUrlTypeFilter(options) {
32323
+ if (options.doi) return "doi";
32324
+ if (options.pubmed) return "pubmed";
32325
+ if (options.pmcid) return "pmcid";
32326
+ if (options.default) return "default";
32327
+ if (options.open) return "default";
32328
+ return null;
32329
+ }
32330
+ function getFilterLabel(filter) {
32331
+ switch (filter) {
32332
+ case "doi":
32333
+ return "DOI";
32334
+ case "pubmed":
32335
+ return "PubMed";
32336
+ case "pmcid":
32337
+ return "PMC";
32338
+ case "url":
32339
+ return "URL";
32340
+ case "default":
32341
+ return "default";
32342
+ }
32343
+ }
32344
+ function resolveUrlsForItem(item, id2, filter) {
32345
+ if (filter === null) {
32346
+ const urls = resolveAllUrls(item);
32347
+ if (urls.length === 0) {
32348
+ return { id: id2, urls: [], error: `No URLs available for ${id2}` };
32349
+ }
32350
+ return { id: id2, urls };
32351
+ }
32352
+ if (filter === "default") {
32353
+ const url22 = resolveDefaultUrl(item);
32354
+ if (!url22) {
32355
+ return { id: id2, urls: [], error: `No URLs available for ${id2}` };
32356
+ }
32357
+ return { id: id2, urls: [url22] };
32358
+ }
32359
+ const url2 = resolveUrlByType(item, filter);
32360
+ if (!url2) {
32361
+ return { id: id2, urls: [], error: `No ${getFilterLabel(filter)} URL for ${id2}` };
32362
+ }
32363
+ return { id: id2, urls: [url2] };
32364
+ }
32365
+ async function executeUrlCommand(identifiers, options, context) {
32366
+ const filter = getUrlTypeFilter(options);
32367
+ const findOptions = options.uuid ? { idType: "uuid" } : void 0;
32368
+ const results = [];
32369
+ for (const identifier of identifiers) {
32370
+ const item = await context.library.find(identifier, findOptions);
32371
+ if (!item) {
32372
+ results.push({ id: identifier, urls: [], error: `Reference not found: ${identifier}` });
32373
+ continue;
32374
+ }
32375
+ const result = resolveUrlsForItem(item, identifier, filter);
32376
+ results.push(result);
32377
+ }
32378
+ if (options.open) {
32379
+ const firstSuccess = results.find((r) => r.urls.length > 0);
32380
+ if (firstSuccess?.urls[0]) {
32381
+ try {
32382
+ await openWithSystemApp(firstSuccess.urls[0]);
32383
+ } catch (error) {
32384
+ return {
32385
+ results,
32386
+ openError: `Failed to open URL: ${error instanceof Error ? error.message : String(error)}`
32387
+ };
32388
+ }
32389
+ }
32390
+ }
32391
+ return { results };
32392
+ }
32393
+ function hasFilter(options) {
32394
+ return Boolean(options.default || options.doi || options.pubmed || options.pmcid || options.open);
32395
+ }
32396
+ function formatUrlOutput(result, options) {
32397
+ const successResults = result.results.filter((r) => r.urls.length > 0);
32398
+ if (successResults.length === 0) {
32399
+ return "";
32400
+ }
32401
+ const filtered = hasFilter(options);
32402
+ if (filtered || successResults.length === 1) {
32403
+ const lines2 = [];
32404
+ for (const r of successResults) {
32405
+ for (const url2 of r.urls) {
32406
+ lines2.push(url2);
32407
+ }
32408
+ }
32409
+ return lines2.join("\n");
32410
+ }
32411
+ const lines = [];
32412
+ for (const r of successResults) {
32413
+ for (const url2 of r.urls) {
32414
+ lines.push(`${r.id} ${url2}`);
32415
+ }
32416
+ }
32417
+ return lines.join("\n");
32418
+ }
32419
+ function formatUrlErrors(result) {
32420
+ const errorMessages = result.results.filter((r) => r.error).map((r) => `Error: ${r.error}`);
32421
+ if (result.openError) {
32422
+ errorMessages.push(`Error: ${result.openError}`);
32423
+ }
32424
+ return errorMessages.join("\n");
32425
+ }
32426
+ function getUrlExitCode(result) {
32427
+ if (result.openError) return ExitCode.ERROR;
32428
+ const hasSuccess = result.results.some((r) => r.urls.length > 0);
32429
+ const hasError = result.results.some((r) => r.error);
32430
+ if (hasSuccess) return ExitCode.SUCCESS;
32431
+ if (hasError) return ExitCode.ERROR;
32432
+ return ExitCode.SUCCESS;
32433
+ }
32434
+ async function executeInteractiveSelect(context, config2) {
32435
+ const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
32436
+ const { selectReferencesOrExit } = await import("./reference-select-DCXlkWpk.js");
32437
+ const allReferences = await context.library.getAll();
32438
+ const identifiers = await withAlternateScreen2(
32439
+ () => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
32440
+ );
32441
+ return identifiers[0] ?? "";
32442
+ }
32443
+ async function handleUrlAction(identifiers, options, globalOpts) {
32444
+ try {
32445
+ const config2 = await loadConfigWithOverrides({ ...globalOpts, ...options });
32446
+ const context = await createExecutionContext(config2, Library.load);
32447
+ let resolvedIdentifiers;
32448
+ let isTuiMode = false;
32449
+ if (identifiers && identifiers.length > 0) {
32450
+ resolvedIdentifiers = identifiers;
32451
+ } else if (isTTY()) {
32452
+ isTuiMode = true;
32453
+ const selected = await executeInteractiveSelect(context, config2);
32454
+ resolvedIdentifiers = [selected];
32455
+ } else {
32456
+ const stdinId = await readIdentifierFromStdin();
32457
+ if (!stdinId) {
32458
+ exitWithError("Identifier is required");
32459
+ return;
32460
+ }
32461
+ resolvedIdentifiers = [stdinId];
32462
+ }
32463
+ const result = await executeUrlCommand(resolvedIdentifiers, options, context);
32464
+ const output = formatUrlOutput(result, options);
32465
+ if (output) {
32466
+ const clipboardEnabled = resolveClipboardEnabled(globalOpts, config2, isTuiMode);
32467
+ await writeOutputWithClipboard(output, clipboardEnabled, config2.logLevel === "silent");
32468
+ }
32469
+ const errors2 = formatUrlErrors(result);
32470
+ if (errors2) {
32471
+ process.stderr.write(`${errors2}
32472
+ `);
32473
+ }
32474
+ setExitCode(getUrlExitCode(result));
32475
+ } catch (error) {
32476
+ process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}
32477
+ `);
32478
+ setExitCode(ExitCode.INTERNAL_ERROR);
32479
+ }
32480
+ }
32024
32481
  const SEARCH_SORT_FIELDS = searchSortFieldSchema.options;
32025
32482
  const SORT_ORDERS = sortOrderSchema.options;
32026
32483
  const CITATION_OUTPUT_FORMATS = ["text", "html", "rtf"];
@@ -32305,7 +32762,7 @@ function registerCompletionCommand(program) {
32305
32762
  function createProgram() {
32306
32763
  const program = new Command();
32307
32764
  program.name("reference-manager").version(packageJson.version).description(packageJson.description);
32308
- program.option("--library <path>", "Override library file path").option("--log-level <level>", "Override log level (silent|info|debug)").option("--config <path>", "Use specific config file").option("--quiet", "Suppress all non-error output").option("--verbose", "Enable verbose output").option("--no-backup", "Disable backup creation").option("--backup-dir <path>", "Override backup directory").option("--attachments-dir <path>", "Override attachments directory");
32765
+ program.option("--library <path>", "Override library file path").option("--log-level <level>", "Override log level (silent|info|debug)").option("--config <path>", "Use specific config file").option("--quiet", "Suppress all non-error output").option("--verbose", "Enable verbose output").option("--no-backup", "Disable backup creation").option("--backup-dir <path>", "Override backup directory").option("--attachments-dir <path>", "Override attachments directory").option("--clipboard", "Copy output to system clipboard").option("--no-clipboard", "Disable clipboard copy");
32309
32766
  registerListCommand(program);
32310
32767
  registerSearchCommand(program);
32311
32768
  registerExportCommand(program);
@@ -32318,6 +32775,7 @@ function createProgram() {
32318
32775
  registerFulltextCommand(program);
32319
32776
  registerAttachCommand(program);
32320
32777
  registerMcpCommand(program);
32778
+ registerUrlCommand(program);
32321
32779
  registerConfigCommand(program);
32322
32780
  registerCompletionCommand(program);
32323
32781
  return program;
@@ -32326,12 +32784,12 @@ async function handleListAction(options, program) {
32326
32784
  try {
32327
32785
  const globalOpts = program.opts();
32328
32786
  const config2 = await loadConfigWithOverrides({ ...globalOpts, ...options });
32787
+ const clipboardEnabled = resolveClipboardEnabled(globalOpts, config2, false);
32329
32788
  const context = await createExecutionContext(config2, Library.load);
32330
32789
  const result = await executeList(options, context);
32331
- const output = formatListOutput(result, options);
32790
+ const output = formatListOutput(result, options, config2.citation.defaultKeyFormat);
32332
32791
  if (output) {
32333
- process.stdout.write(`${output}
32334
- `);
32792
+ await writeOutputWithClipboard(output, clipboardEnabled, config2.logLevel === "silent");
32335
32793
  }
32336
32794
  setExitCode(ExitCode.SUCCESS);
32337
32795
  } catch (error) {
@@ -32339,7 +32797,10 @@ async function handleListAction(options, program) {
32339
32797
  }
32340
32798
  }
32341
32799
  function registerListCommand(program) {
32342
- program.command("list").description("List all references in the library").option("-o, --output <format>", "Output format: pretty|json|bibtex|ids|uuid").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("--sort <field>", "Sort by field: created|updated|published|author|title").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 (options) => {
32800
+ program.command("list").description("List all references in the library").option(
32801
+ "-o, --output <format>",
32802
+ "Output format: pretty|json|bibtex|ids|uuid|pandoc-key|latex-key"
32803
+ ).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").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 (options) => {
32343
32804
  await handleListAction(options, program);
32344
32805
  });
32345
32806
  }
@@ -32347,12 +32808,12 @@ async function handleExportAction(ids, options, program) {
32347
32808
  try {
32348
32809
  const globalOpts = program.opts();
32349
32810
  const config2 = await loadConfigWithOverrides({ ...globalOpts, ...options });
32811
+ const clipboardEnabled = resolveClipboardEnabled(globalOpts, config2, false);
32350
32812
  const context = await createExecutionContext(config2, Library.load);
32351
32813
  const result = await executeExport({ ...options, ids }, context);
32352
32814
  const output = formatExportOutput(result, { ...options, ids });
32353
32815
  if (output) {
32354
- process.stdout.write(`${output}
32355
- `);
32816
+ await writeOutputWithClipboard(output, clipboardEnabled, config2.logLevel === "silent");
32356
32817
  }
32357
32818
  if (result.notFound.length > 0) {
32358
32819
  for (const id2 of result.notFound) {
@@ -32380,17 +32841,25 @@ async function handleSearchAction(query, options, program) {
32380
32841
  if (options.tui) {
32381
32842
  const result2 = await executeInteractiveSearch({ ...options, query }, context, config2);
32382
32843
  if (result2.output) {
32383
- process.stdout.write(`${result2.output}
32384
- `);
32844
+ const clipboardEnabled = resolveClipboardEnabled(globalOpts, config2, true);
32845
+ await writeOutputWithClipboard(
32846
+ result2.output,
32847
+ clipboardEnabled,
32848
+ config2.logLevel === "silent"
32849
+ );
32385
32850
  }
32386
32851
  setExitCode(ExitCode.SUCCESS);
32387
32852
  return;
32388
32853
  }
32389
32854
  const result = await executeSearch({ ...options, query }, context);
32390
- const output = formatSearchOutput(result, { ...options, query });
32855
+ const output = formatSearchOutput(
32856
+ result,
32857
+ { ...options, query },
32858
+ config2.citation.defaultKeyFormat
32859
+ );
32391
32860
  if (output) {
32392
- process.stdout.write(`${output}
32393
- `);
32861
+ const clipboardEnabled = resolveClipboardEnabled(globalOpts, config2, false);
32862
+ await writeOutputWithClipboard(output, clipboardEnabled, config2.logLevel === "silent");
32394
32863
  }
32395
32864
  setExitCode(ExitCode.SUCCESS);
32396
32865
  } catch (error) {
@@ -32400,7 +32869,10 @@ async function handleSearchAction(query, options, program) {
32400
32869
  }
32401
32870
  }
32402
32871
  function registerSearchCommand(program) {
32403
- program.command("search").description("Search references").argument("[query]", "Search query (required unless using --tui)").option("-t, --tui", "Enable TUI (interactive) search mode").option("-o, --output <format>", "Output format: pretty|json|bibtex|ids|uuid").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("--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) => {
32872
+ program.command("search").description("Search references").argument("[query]", "Search query (required unless using --tui)").option("-t, --tui", "Enable TUI (interactive) search mode").option(
32873
+ "-o, --output <format>",
32874
+ "Output format: pretty|json|bibtex|ids|uuid|pandoc-key|latex-key"
32875
+ ).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) => {
32404
32876
  if (!options.tui && !query) {
32405
32877
  process.stderr.write("Error: Search query is required unless using --tui\n");
32406
32878
  setExitCode(ExitCode.ERROR);
@@ -32616,6 +33088,9 @@ function registerMcpCommand(program) {
32616
33088
  }
32617
33089
  function registerAttachCommand(program) {
32618
33090
  const attachCmd = program.command("attach").description("Manage file attachments for references");
33091
+ attachCmd.argument("[identifier]", "Citation key or UUID (interactive selection if omitted)").option("--uuid", "Interpret identifier as UUID").action(async (identifier, options) => {
33092
+ await handleAttachOpenAction(identifier, void 0, options, program.opts());
33093
+ });
32619
33094
  attachCmd.command("open").description("Open attachments directory or specific file").argument("[identifier]", "Citation key or UUID (interactive selection if omitted)").argument("[filename]", "Specific file to open").option("--role <role>", "Open file by role").option("--print", "Output path instead of opening").option("--no-sync", "Skip interactive sync prompt").option("--uuid", "Interpret identifier as UUID").action(async (identifier, filename, options) => {
32620
33095
  await handleAttachOpenAction(identifier, filename, options, program.opts());
32621
33096
  });
@@ -32653,6 +33128,12 @@ function registerFulltextCommand(program) {
32653
33128
  await handleFulltextOpenAction(identifier, options, program.opts());
32654
33129
  });
32655
33130
  }
33131
+ function registerUrlCommand(program) {
33132
+ program.command("url").description("Show URLs for references").argument("[identifiers...]", "Reference identifiers").option("--default", "Show only the best URL by priority").option("--doi", "Show only DOI URL").option("--pubmed", "Show only PubMed URL").option("--pmcid", "Show only PMC URL").option("--open", "Open URL in browser").option("--uuid", "Interpret identifiers as UUIDs").action(async (identifiers, options) => {
33133
+ const globalOpts = program.opts();
33134
+ await handleUrlAction(identifiers.length > 0 ? identifiers : void 0, options, globalOpts);
33135
+ });
33136
+ }
32656
33137
  async function main(argv) {
32657
33138
  const program = createProgram();
32658
33139
  if (process.env.COMP_LINE) {
@@ -32670,6 +33151,7 @@ async function main(argv) {
32670
33151
  export {
32671
33152
  Select as S,
32672
33153
  addAttachment as a,
33154
+ stringify as b,
32673
33155
  createProgram as c,
32674
33156
  detachAttachment as d,
32675
33157
  formatBibtex as f,
@@ -32681,4 +33163,4 @@ export {
32681
33163
  restoreStdinAfterInk as r,
32682
33164
  syncAttachments as s
32683
33165
  };
32684
- //# sourceMappingURL=index-wb8zgPJ0.js.map
33166
+ //# sourceMappingURL=index-vInTzmDN.js.map