@ncukondo/reference-manager 0.5.3 → 0.7.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 (87) hide show
  1. package/README.md +93 -0
  2. package/dist/chunks/action-menu-CTtINmWd.js +118 -0
  3. package/dist/chunks/action-menu-CTtINmWd.js.map +1 -0
  4. package/dist/chunks/{file-watcher-CBAbblss.js → file-watcher-D7oyc-9z.js} +55 -11
  5. package/dist/chunks/file-watcher-D7oyc-9z.js.map +1 -0
  6. package/dist/chunks/{index-Bl_mOQRe.js → index-_7NEUoS7.js} +271 -136
  7. package/dist/chunks/index-_7NEUoS7.js.map +1 -0
  8. package/dist/chunks/{loader-DuzyKV70.js → loader-BItrdVWG.js} +149 -26
  9. package/dist/chunks/loader-BItrdVWG.js.map +1 -0
  10. package/dist/chunks/search-prompt-D67WyKrY.js +179 -0
  11. package/dist/chunks/search-prompt-D67WyKrY.js.map +1 -0
  12. package/dist/chunks/tty-CDBIQraQ.js +17 -0
  13. package/dist/chunks/tty-CDBIQraQ.js.map +1 -0
  14. package/dist/cli/commands/list.d.ts +7 -1
  15. package/dist/cli/commands/list.d.ts.map +1 -1
  16. package/dist/cli/commands/search.d.ts +26 -1
  17. package/dist/cli/commands/search.d.ts.map +1 -1
  18. package/dist/cli/completion.d.ts +65 -0
  19. package/dist/cli/completion.d.ts.map +1 -0
  20. package/dist/cli/index.d.ts.map +1 -1
  21. package/dist/cli.js +535 -119
  22. package/dist/cli.js.map +1 -1
  23. package/dist/config/defaults.d.ts.map +1 -1
  24. package/dist/config/loader.d.ts.map +1 -1
  25. package/dist/config/schema.d.ts +97 -0
  26. package/dist/config/schema.d.ts.map +1 -1
  27. package/dist/features/import/importer.d.ts.map +1 -1
  28. package/dist/features/interactive/action-menu.d.ts +56 -0
  29. package/dist/features/interactive/action-menu.d.ts.map +1 -0
  30. package/dist/features/interactive/debounce.d.ts +22 -0
  31. package/dist/features/interactive/debounce.d.ts.map +1 -0
  32. package/dist/features/interactive/format.d.ts +52 -0
  33. package/dist/features/interactive/format.d.ts.map +1 -0
  34. package/dist/features/interactive/search-prompt.d.ts +47 -0
  35. package/dist/features/interactive/search-prompt.d.ts.map +1 -0
  36. package/dist/features/interactive/tty.d.ts +20 -0
  37. package/dist/features/interactive/tty.d.ts.map +1 -0
  38. package/dist/features/operations/add.d.ts.map +1 -1
  39. package/dist/features/operations/list.d.ts +12 -3
  40. package/dist/features/operations/list.d.ts.map +1 -1
  41. package/dist/features/operations/search.d.ts +19 -3
  42. package/dist/features/operations/search.d.ts.map +1 -1
  43. package/dist/features/pagination/aliases.d.ts +14 -0
  44. package/dist/features/pagination/aliases.d.ts.map +1 -0
  45. package/dist/features/pagination/index.d.ts +8 -0
  46. package/dist/features/pagination/index.d.ts.map +1 -0
  47. package/dist/features/pagination/paginate.d.ts +20 -0
  48. package/dist/features/pagination/paginate.d.ts.map +1 -0
  49. package/dist/features/pagination/sorter.d.ts +16 -0
  50. package/dist/features/pagination/sorter.d.ts.map +1 -0
  51. package/dist/features/pagination/types.d.ts +74 -0
  52. package/dist/features/pagination/types.d.ts.map +1 -0
  53. package/dist/index.js +13 -12
  54. package/dist/index.js.map +1 -1
  55. package/dist/mcp/context.d.ts +4 -4
  56. package/dist/mcp/context.d.ts.map +1 -1
  57. package/dist/mcp/resources/index.d.ts +3 -3
  58. package/dist/mcp/resources/index.d.ts.map +1 -1
  59. package/dist/mcp/resources/library.d.ts +5 -5
  60. package/dist/mcp/resources/library.d.ts.map +1 -1
  61. package/dist/mcp/tools/add.d.ts +3 -3
  62. package/dist/mcp/tools/add.d.ts.map +1 -1
  63. package/dist/mcp/tools/cite.d.ts +3 -3
  64. package/dist/mcp/tools/cite.d.ts.map +1 -1
  65. package/dist/mcp/tools/fulltext.d.ts +7 -7
  66. package/dist/mcp/tools/fulltext.d.ts.map +1 -1
  67. package/dist/mcp/tools/index.d.ts +3 -3
  68. package/dist/mcp/tools/index.d.ts.map +1 -1
  69. package/dist/mcp/tools/list.d.ts +10 -3
  70. package/dist/mcp/tools/list.d.ts.map +1 -1
  71. package/dist/mcp/tools/remove.d.ts +3 -3
  72. package/dist/mcp/tools/remove.d.ts.map +1 -1
  73. package/dist/mcp/tools/search.d.ts +10 -3
  74. package/dist/mcp/tools/search.d.ts.map +1 -1
  75. package/dist/server/routes/list.d.ts +13 -0
  76. package/dist/server/routes/list.d.ts.map +1 -1
  77. package/dist/server/routes/search.d.ts +14 -0
  78. package/dist/server/routes/search.d.ts.map +1 -1
  79. package/dist/server.js +4 -4
  80. package/dist/utils/index.d.ts +1 -0
  81. package/dist/utils/index.d.ts.map +1 -1
  82. package/dist/utils/object.d.ts +16 -0
  83. package/dist/utils/object.d.ts.map +1 -0
  84. package/package.json +4 -1
  85. package/dist/chunks/file-watcher-CBAbblss.js.map +0 -1
  86. package/dist/chunks/index-Bl_mOQRe.js.map +0 -1
  87. package/dist/chunks/loader-DuzyKV70.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/features/pagination/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;EAAiE,CAAC;AAC9F,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;;;EAOhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;GAEG;AACH,eAAO,MAAM,eAAe;;;EAA0B,CAAC;AACvD,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;iBAGlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;iBAG5B,CAAC;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,IAAI;IACtD,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B"}
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
- import { C as CslLibrarySchema, c as computeFileHash, a as computeHash, n as normalize, s as search, b as sortResults, t as tokenize, d as detectDuplicate } from "./chunks/file-watcher-CBAbblss.js";
2
- import { e, F, L, R, l, m, g, h, j, k, i, p, f, w } from "./chunks/file-watcher-CBAbblss.js";
1
+ import { C as CslLibrarySchema, c as computeFileHash, a as computeHash, p as pickDefined, n as normalize, s as search, b as sortResults, t as tokenize, d as detectDuplicate } from "./chunks/file-watcher-D7oyc-9z.js";
2
+ import { e, F, L, R, m, o, h, i, k, l, j, f, g, w } from "./chunks/file-watcher-D7oyc-9z.js";
3
3
  import { mkdir, readFile, copyFile, stat, unlink, readdir } from "node:fs/promises";
4
4
  import { dirname, join } from "node:path";
5
5
  import writeFileAtomicLib from "write-file-atomic";
6
6
  import { existsSync } from "node:fs";
7
7
  import { tmpdir } from "node:os";
8
8
  import { fileURLToPath } from "node:url";
9
- import { b as backupConfigSchema, c as configSchema, d as defaultConfig, g as getDefaultBackupDirectory, a as getDefaultCurrentDirConfigFilename, e as getDefaultLibraryPath, f as getDefaultUserConfigPath, l as loadConfig, h as logLevelSchema, n as normalizePartialConfig, p as partialConfigSchema, w as watchConfigSchema } from "./chunks/loader-DuzyKV70.js";
9
+ import { b as backupConfigSchema, c as configSchema, d as defaultConfig, g as getDefaultBackupDirectory, a as getDefaultCurrentDirConfigFilename, e as getDefaultLibraryPath, f as getDefaultUserConfigPath, l as loadConfig, h as logLevelSchema, n as normalizePartialConfig, p as partialConfigSchema, w as watchConfigSchema } from "./chunks/loader-BItrdVWG.js";
10
10
  function validateCslJson(data) {
11
11
  const parseResult = CslLibrarySchema.safeParse(data);
12
12
  if (!parseResult.success) {
@@ -120,6 +120,7 @@ const index$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
120
120
  ensureDirectoryExists,
121
121
  getBackupDirectory,
122
122
  listBackups,
123
+ pickDefined,
123
124
  writeFileAtomic
124
125
  }, Symbol.toStringTag, { value: "Module" }));
125
126
  const index$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
@@ -378,15 +379,15 @@ export {
378
379
  R as Reference,
379
380
  index$2 as Search,
380
381
  index$4 as Utils,
381
- l as ensureCustomMetadata,
382
- m as extractUuidFromCustom,
383
- g as generateId,
384
- h as generateIdWithCollisionCheck,
385
- j as generateUuid,
386
- k as isValidUuid,
387
- i as normalizeText,
388
- p as parseCslJson,
389
- f as serializeCslJson,
382
+ m as ensureCustomMetadata,
383
+ o as extractUuidFromCustom,
384
+ h as generateId,
385
+ i as generateIdWithCollisionCheck,
386
+ k as generateUuid,
387
+ l as isValidUuid,
388
+ j as normalizeText,
389
+ f as parseCslJson,
390
+ g as serializeCslJson,
390
391
  validateCslJson,
391
392
  w as writeCslJson
392
393
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/core/csl-json/validator.ts","../src/utils/logger.ts","../src/utils/file.ts","../src/utils/backup.ts","../src/features/merge/three-way.ts"],"sourcesContent":["import { CslItemSchema, CslLibrarySchema } from \"./types\";\nimport type { CslItem, CslLibrary } from \"./types\";\n\n/**\n * Validate CSL-JSON library structure\n * @param data - Data to validate (can be any type)\n * @returns Validated CSL-JSON library\n * @throws Error if validation fails\n */\nexport function validateCslJson(data: unknown): CslLibrary {\n const parseResult = CslLibrarySchema.safeParse(data);\n\n if (!parseResult.success) {\n throw new Error(`Invalid CSL-JSON structure: ${parseResult.error.message}`);\n }\n\n return parseResult.data;\n}\n\n/**\n * Validate a single CSL-JSON item\n * @param data - Data to validate (can be any type)\n * @returns Validation result with valid flag and errors\n */\nexport function validateCslItem(data: unknown): {\n valid: boolean;\n data?: CslItem;\n errors?: string[];\n} {\n const parseResult = CslItemSchema.safeParse(data);\n\n if (!parseResult.success) {\n return {\n valid: false,\n errors: parseResult.error.issues.map((issue) => issue.message),\n };\n }\n\n return {\n valid: true,\n data: parseResult.data,\n };\n}\n","export type LogLevel = \"silent\" | \"info\" | \"debug\";\n\nexport interface Logger {\n info(...args: unknown[]): void;\n debug(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\nexport function createLogger(level: LogLevel = \"info\"): Logger {\n const shouldLogInfo = level === \"info\" || level === \"debug\";\n const shouldLogDebug = level === \"debug\";\n\n function formatMessage(...args: unknown[]): string {\n return `${args.map((arg) => String(arg)).join(\" \")}\\n`;\n }\n\n return {\n info(...args: unknown[]): void {\n if (shouldLogInfo) {\n process.stderr.write(formatMessage(...args));\n }\n },\n\n debug(...args: unknown[]): void {\n if (shouldLogDebug) {\n process.stderr.write(formatMessage(...args));\n }\n },\n\n error(...args: unknown[]): void {\n process.stderr.write(formatMessage(...args));\n },\n };\n}\n","import { mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport writeFileAtomicLib from \"write-file-atomic\";\n\n/**\n * Write file atomically with parent directory creation\n */\nexport async function writeFileAtomic(filePath: string, content: string): Promise<void> {\n await ensureDirectoryExists(dirname(filePath));\n await writeFileAtomicLib(filePath, content, { encoding: \"utf-8\" });\n}\n\n/**\n * Ensure directory exists, creating it recursively if necessary\n */\nexport async function ensureDirectoryExists(dirPath: string): Promise<void> {\n await mkdir(dirPath, { recursive: true });\n}\n","import { existsSync } from \"node:fs\";\nimport { copyFile, readFile, readdir, stat, unlink } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { ensureDirectoryExists } from \"./file\";\n\nexport interface BackupOptions {\n maxGenerations?: number;\n maxAgeMs?: number;\n}\n\nconst DEFAULT_MAX_GENERATIONS = 50;\nconst DEFAULT_MAX_AGE_MS = 365 * 24 * 60 * 60 * 1000; // 1 year\n\n/**\n * Get package name from package.json\n */\nasync function resolvePackageName(): Promise<string> {\n try {\n const currentFile = fileURLToPath(import.meta.url);\n let currentDir = dirname(currentFile);\n\n for (let i = 0; i < 10; i++) {\n const packageJsonPath = join(currentDir, \"package.json\");\n if (existsSync(packageJsonPath)) {\n const content = await readFile(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content);\n return pkg.name;\n }\n currentDir = dirname(currentDir);\n }\n } catch {\n // Fall back to hardcoded name if package.json is not found\n }\n\n return \"reference-manager\";\n}\n\nconst packageName = await resolvePackageName();\n\n/**\n * Get backup directory path for a library\n */\nexport function getBackupDirectory(libraryName: string): string {\n const pkgName = packageName ?? \"reference-manager\";\n return join(tmpdir(), pkgName, \"backups\", libraryName);\n}\n\n/**\n * Create a backup of the given file\n */\nexport async function createBackup(sourceFile: string, libraryName: string): Promise<string> {\n const backupDir = getBackupDirectory(libraryName);\n await ensureDirectoryExists(backupDir);\n\n const timestamp = Date.now();\n const backupFileName = `${timestamp}.backup`;\n const backupPath = join(backupDir, backupFileName);\n\n await copyFile(sourceFile, backupPath);\n\n return backupPath;\n}\n\n/**\n * List all backups for a library (sorted by modification time, newest first)\n */\nexport async function listBackups(libraryName: string): Promise<string[]> {\n const backupDir = getBackupDirectory(libraryName);\n\n if (!existsSync(backupDir)) {\n return [];\n }\n\n const files = await readdir(backupDir);\n const backupFiles = files.filter((f) => f.endsWith(\".backup\")).map((f) => join(backupDir, f));\n\n const filesWithStats = await Promise.all(\n backupFiles.map(async (file) => {\n const stats = await stat(file);\n return { file, mtime: stats.mtimeMs };\n })\n );\n\n filesWithStats.sort((a, b) => b.mtime - a.mtime);\n\n return filesWithStats.map((f) => f.file);\n}\n\n/**\n * Clean up old backups based on generation count and age\n */\nexport async function cleanupOldBackups(\n libraryName: string,\n options?: BackupOptions\n): Promise<void> {\n const maxGenerations = options?.maxGenerations ?? DEFAULT_MAX_GENERATIONS;\n const maxAgeMs = options?.maxAgeMs ?? DEFAULT_MAX_AGE_MS;\n\n const backups = await listBackups(libraryName);\n const now = Date.now();\n\n const backupsToDelete: string[] = [];\n\n for (let i = 0; i < backups.length; i++) {\n const backupPath = backups[i];\n if (!backupPath) continue;\n\n const stats = await stat(backupPath);\n const age = now - stats.mtimeMs;\n\n if (i >= maxGenerations || age > maxAgeMs) {\n backupsToDelete.push(backupPath);\n }\n }\n\n await Promise.all(backupsToDelete.map((backup) => unlink(backup)));\n}\n","/**\n * 3-way merge implementation with Last-Write-Wins (LWW) strategy\n */\n\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport type {\n FieldConflict,\n ItemConflict,\n MergeOptions,\n MergeResult,\n MergeStatus,\n} from \"./types.js\";\n\n/**\n * Get UUID from item, with fallback to id if uuid is missing\n */\nfunction getItemUuid(item: CslItem): string {\n return item.custom?.uuid || item.id || \"unknown\";\n}\n\n/**\n * Get timestamp from item, with fallback to created_at\n */\nfunction getTimestamp(item: CslItem): string {\n return item.custom?.timestamp || item.custom?.created_at || \"1970-01-01T00:00:00.000Z\";\n}\n\n/**\n * Deep equality check for field values\n */\nfunction deepEqual(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\n/**\n * Resolve a field conflict using LWW or prefer option\n */\nfunction resolveFieldConflict(\n localValue: unknown,\n remoteValue: unknown,\n localTimestamp: string,\n remoteTimestamp: string,\n options?: MergeOptions\n): unknown {\n if (localTimestamp > remoteTimestamp) {\n return localValue;\n }\n if (remoteTimestamp > localTimestamp) {\n return remoteValue;\n }\n // Timestamps equal: use prefer option or default to local\n if (options?.prefer === \"remote\") {\n return remoteValue;\n }\n return localValue;\n}\n\n/**\n * Determine conflict resolution type\n */\nfunction determineResolution(\n fieldConflicts: FieldConflict[],\n localTimestamp: string,\n remoteTimestamp: string,\n options?: MergeOptions\n): ItemConflict[\"resolution\"] {\n const hasRealConflicts = fieldConflicts.every((fc) => fc.resolved !== undefined);\n const localIsNewer = fieldConflicts.some(\n (fc) => fc.local !== fc.remote && localTimestamp > remoteTimestamp\n );\n const remoteIsNewer = fieldConflicts.some(\n (fc) => fc.local !== fc.remote && remoteTimestamp > localTimestamp\n );\n\n if (hasRealConflicts && localIsNewer) return \"auto-lww\";\n if (hasRealConflicts && remoteIsNewer) return \"auto-lww\";\n if (options?.prefer === \"local\") return \"prefer-local\";\n if (options?.prefer === \"remote\") return \"prefer-remote\";\n return \"unresolved\";\n}\n\n/**\n * Merge a single field from base, local, and remote versions\n */\nfunction mergeField(\n key: string,\n baseValue: unknown,\n localValue: unknown,\n remoteValue: unknown,\n localTimestamp: string,\n remoteTimestamp: string,\n options?: MergeOptions\n): { value: unknown; conflict: FieldConflict | null } {\n const localChanged = !deepEqual(baseValue, localValue);\n const remoteChanged = !deepEqual(baseValue, remoteValue);\n\n if (!localChanged && !remoteChanged) {\n return { value: baseValue, conflict: null };\n }\n\n if (localChanged && !remoteChanged) {\n return { value: localValue, conflict: null };\n }\n\n if (!localChanged && remoteChanged) {\n return { value: remoteValue, conflict: null };\n }\n\n // Both changed\n if (deepEqual(localValue, remoteValue)) {\n return { value: localValue, conflict: null };\n }\n\n // Both changed to different values\n const resolved = resolveFieldConflict(\n localValue,\n remoteValue,\n localTimestamp,\n remoteTimestamp,\n options\n );\n\n // Don't record conflicts for 'custom' metadata field\n if (key === \"custom\") {\n return { value: resolved, conflict: null };\n }\n\n return {\n value: resolved,\n conflict: {\n field: key,\n base: baseValue,\n local: localValue,\n remote: remoteValue,\n resolved,\n },\n };\n}\n\n/**\n * Merge a single item from base, local, and remote versions\n */\nfunction mergeItem(\n base: CslItem,\n local: CslItem,\n remote: CslItem,\n options?: MergeOptions\n): { merged: CslItem; conflict: ItemConflict | null } {\n const uuid = getItemUuid(base);\n const localTimestamp = getTimestamp(local);\n const remoteTimestamp = getTimestamp(remote);\n\n const merged: CslItem = { ...base };\n const fieldConflicts: FieldConflict[] = [];\n\n // Get all unique keys from all three versions\n const allKeys = new Set<string>([\n ...Object.keys(base),\n ...Object.keys(local),\n ...Object.keys(remote),\n ]);\n\n for (const key of allKeys) {\n const baseValue = (base as Record<string, unknown>)[key];\n const localValue = (local as Record<string, unknown>)[key];\n const remoteValue = (remote as Record<string, unknown>)[key];\n\n const { value, conflict } = mergeField(\n key,\n baseValue,\n localValue,\n remoteValue,\n localTimestamp,\n remoteTimestamp,\n options\n );\n\n (merged as Record<string, unknown>)[key] = value;\n\n if (conflict) {\n fieldConflicts.push(conflict);\n }\n }\n\n // If there are field conflicts, create ItemConflict\n if (fieldConflicts.length > 0) {\n const resolution = determineResolution(\n fieldConflicts,\n localTimestamp,\n remoteTimestamp,\n options\n );\n\n return {\n merged,\n conflict: {\n uuid,\n id: base.id || \"unknown\",\n fields: fieldConflicts,\n localTimestamp,\n remoteTimestamp,\n resolution,\n },\n };\n }\n\n return { merged, conflict: null };\n}\n\n/**\n * Build UUID-indexed maps from item arrays\n */\nfunction buildItemMaps(base: CslItem[], local: CslItem[], remote: CslItem[]) {\n const baseMap = new Map<string, CslItem>();\n const localMap = new Map<string, CslItem>();\n const remoteMap = new Map<string, CslItem>();\n\n for (const item of base) {\n baseMap.set(getItemUuid(item), item);\n }\n for (const item of local) {\n localMap.set(getItemUuid(item), item);\n }\n for (const item of remote) {\n remoteMap.set(getItemUuid(item), item);\n }\n\n return { baseMap, localMap, remoteMap };\n}\n\n/**\n * Handle items that exist in all three versions\n */\nfunction mergeExistingItem(\n baseItem: CslItem,\n localItem: CslItem,\n remoteItem: CslItem,\n options: MergeOptions | undefined,\n merged: CslItem[],\n conflicts: ItemConflict[]\n): void {\n const { merged: mergedItem, conflict } = mergeItem(baseItem, localItem, remoteItem, options);\n merged.push(mergedItem);\n if (conflict) {\n conflicts.push(conflict);\n }\n}\n\n/**\n * Handle items added in both local and remote\n */\nfunction handleDualAddition(\n uuid: string,\n localItem: CslItem,\n remoteItem: CslItem,\n options: MergeOptions | undefined,\n merged: CslItem[],\n conflicts: ItemConflict[]\n): void {\n if (deepEqual(localItem, remoteItem)) {\n merged.push(localItem);\n } else {\n const syntheticBase: CslItem = {\n id: uuid,\n type: \"article\",\n custom: {\n uuid,\n created_at: \"1970-01-01T00:00:00.000Z\",\n timestamp: \"1970-01-01T00:00:00.000Z\",\n },\n };\n const { merged: mergedItem, conflict } = mergeItem(\n syntheticBase,\n localItem,\n remoteItem,\n options\n );\n merged.push(mergedItem);\n if (conflict) {\n conflicts.push(conflict);\n }\n }\n}\n\n/**\n * Process a single UUID across all three versions\n */\nfunction processItem(\n uuid: string,\n baseMap: Map<string, CslItem>,\n localMap: Map<string, CslItem>,\n remoteMap: Map<string, CslItem>,\n options: MergeOptions | undefined,\n result: {\n merged: CslItem[];\n conflicts: ItemConflict[];\n localOnly: CslItem[];\n remoteOnly: CslItem[];\n deletedInLocal: CslItem[];\n deletedInRemote: CslItem[];\n }\n): void {\n const baseItem = baseMap.get(uuid);\n const localItem = localMap.get(uuid);\n const remoteItem = remoteMap.get(uuid);\n\n if (baseItem && localItem && remoteItem) {\n mergeExistingItem(baseItem, localItem, remoteItem, options, result.merged, result.conflicts);\n } else if (!baseItem && localItem && remoteItem) {\n handleDualAddition(uuid, localItem, remoteItem, options, result.merged, result.conflicts);\n } else if (!baseItem && localItem && !remoteItem) {\n result.merged.push(localItem);\n result.localOnly.push(localItem);\n } else if (!baseItem && !localItem && remoteItem) {\n result.merged.push(remoteItem);\n result.remoteOnly.push(remoteItem);\n } else if (baseItem && !localItem && remoteItem) {\n result.deletedInLocal.push(baseItem);\n } else if (baseItem && localItem && !remoteItem) {\n result.deletedInRemote.push(baseItem);\n } else if (baseItem && !localItem && !remoteItem) {\n result.deletedInLocal.push(baseItem);\n result.deletedInRemote.push(baseItem);\n }\n}\n\n/**\n * Performs a 3-way merge of CSL-JSON items\n *\n * @param base - Base version (common ancestor)\n * @param local - Local version (current working copy)\n * @param remote - Remote version (incoming changes)\n * @param options - Merge options (e.g., prefer local/remote for tie-breaking)\n * @returns Merge result with merged items and conflict information\n */\nexport function threeWayMerge(\n base: CslItem[],\n local: CslItem[],\n remote: CslItem[],\n options?: MergeOptions\n): MergeResult {\n const { baseMap, localMap, remoteMap } = buildItemMaps(base, local, remote);\n\n const result = {\n merged: [] as CslItem[],\n conflicts: [] as ItemConflict[],\n localOnly: [] as CslItem[],\n remoteOnly: [] as CslItem[],\n deletedInLocal: [] as CslItem[],\n deletedInRemote: [] as CslItem[],\n };\n\n const allUuids = new Set<string>([...baseMap.keys(), ...localMap.keys(), ...remoteMap.keys()]);\n\n for (const uuid of allUuids) {\n processItem(uuid, baseMap, localMap, remoteMap, options, result);\n }\n\n // Determine overall status\n let status: MergeStatus = \"success\";\n if (result.conflicts.length > 0) {\n const hasUnresolved = result.conflicts.some((c) => c.resolution === \"unresolved\");\n status = hasUnresolved ? \"conflict\" : \"auto-resolved\";\n }\n\n return {\n status,\n ...result,\n };\n}\n"],"names":["i","f"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/core/csl-json/validator.ts","../src/utils/logger.ts","../src/utils/file.ts","../src/utils/backup.ts","../src/features/merge/three-way.ts"],"sourcesContent":["import { CslItemSchema, CslLibrarySchema } from \"./types\";\nimport type { CslItem, CslLibrary } from \"./types\";\n\n/**\n * Validate CSL-JSON library structure\n * @param data - Data to validate (can be any type)\n * @returns Validated CSL-JSON library\n * @throws Error if validation fails\n */\nexport function validateCslJson(data: unknown): CslLibrary {\n const parseResult = CslLibrarySchema.safeParse(data);\n\n if (!parseResult.success) {\n throw new Error(`Invalid CSL-JSON structure: ${parseResult.error.message}`);\n }\n\n return parseResult.data;\n}\n\n/**\n * Validate a single CSL-JSON item\n * @param data - Data to validate (can be any type)\n * @returns Validation result with valid flag and errors\n */\nexport function validateCslItem(data: unknown): {\n valid: boolean;\n data?: CslItem;\n errors?: string[];\n} {\n const parseResult = CslItemSchema.safeParse(data);\n\n if (!parseResult.success) {\n return {\n valid: false,\n errors: parseResult.error.issues.map((issue) => issue.message),\n };\n }\n\n return {\n valid: true,\n data: parseResult.data,\n };\n}\n","export type LogLevel = \"silent\" | \"info\" | \"debug\";\n\nexport interface Logger {\n info(...args: unknown[]): void;\n debug(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\nexport function createLogger(level: LogLevel = \"info\"): Logger {\n const shouldLogInfo = level === \"info\" || level === \"debug\";\n const shouldLogDebug = level === \"debug\";\n\n function formatMessage(...args: unknown[]): string {\n return `${args.map((arg) => String(arg)).join(\" \")}\\n`;\n }\n\n return {\n info(...args: unknown[]): void {\n if (shouldLogInfo) {\n process.stderr.write(formatMessage(...args));\n }\n },\n\n debug(...args: unknown[]): void {\n if (shouldLogDebug) {\n process.stderr.write(formatMessage(...args));\n }\n },\n\n error(...args: unknown[]): void {\n process.stderr.write(formatMessage(...args));\n },\n };\n}\n","import { mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport writeFileAtomicLib from \"write-file-atomic\";\n\n/**\n * Write file atomically with parent directory creation\n */\nexport async function writeFileAtomic(filePath: string, content: string): Promise<void> {\n await ensureDirectoryExists(dirname(filePath));\n await writeFileAtomicLib(filePath, content, { encoding: \"utf-8\" });\n}\n\n/**\n * Ensure directory exists, creating it recursively if necessary\n */\nexport async function ensureDirectoryExists(dirPath: string): Promise<void> {\n await mkdir(dirPath, { recursive: true });\n}\n","import { existsSync } from \"node:fs\";\nimport { copyFile, readFile, readdir, stat, unlink } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { ensureDirectoryExists } from \"./file\";\n\nexport interface BackupOptions {\n maxGenerations?: number;\n maxAgeMs?: number;\n}\n\nconst DEFAULT_MAX_GENERATIONS = 50;\nconst DEFAULT_MAX_AGE_MS = 365 * 24 * 60 * 60 * 1000; // 1 year\n\n/**\n * Get package name from package.json\n */\nasync function resolvePackageName(): Promise<string> {\n try {\n const currentFile = fileURLToPath(import.meta.url);\n let currentDir = dirname(currentFile);\n\n for (let i = 0; i < 10; i++) {\n const packageJsonPath = join(currentDir, \"package.json\");\n if (existsSync(packageJsonPath)) {\n const content = await readFile(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content);\n return pkg.name;\n }\n currentDir = dirname(currentDir);\n }\n } catch {\n // Fall back to hardcoded name if package.json is not found\n }\n\n return \"reference-manager\";\n}\n\nconst packageName = await resolvePackageName();\n\n/**\n * Get backup directory path for a library\n */\nexport function getBackupDirectory(libraryName: string): string {\n const pkgName = packageName ?? \"reference-manager\";\n return join(tmpdir(), pkgName, \"backups\", libraryName);\n}\n\n/**\n * Create a backup of the given file\n */\nexport async function createBackup(sourceFile: string, libraryName: string): Promise<string> {\n const backupDir = getBackupDirectory(libraryName);\n await ensureDirectoryExists(backupDir);\n\n const timestamp = Date.now();\n const backupFileName = `${timestamp}.backup`;\n const backupPath = join(backupDir, backupFileName);\n\n await copyFile(sourceFile, backupPath);\n\n return backupPath;\n}\n\n/**\n * List all backups for a library (sorted by modification time, newest first)\n */\nexport async function listBackups(libraryName: string): Promise<string[]> {\n const backupDir = getBackupDirectory(libraryName);\n\n if (!existsSync(backupDir)) {\n return [];\n }\n\n const files = await readdir(backupDir);\n const backupFiles = files.filter((f) => f.endsWith(\".backup\")).map((f) => join(backupDir, f));\n\n const filesWithStats = await Promise.all(\n backupFiles.map(async (file) => {\n const stats = await stat(file);\n return { file, mtime: stats.mtimeMs };\n })\n );\n\n filesWithStats.sort((a, b) => b.mtime - a.mtime);\n\n return filesWithStats.map((f) => f.file);\n}\n\n/**\n * Clean up old backups based on generation count and age\n */\nexport async function cleanupOldBackups(\n libraryName: string,\n options?: BackupOptions\n): Promise<void> {\n const maxGenerations = options?.maxGenerations ?? DEFAULT_MAX_GENERATIONS;\n const maxAgeMs = options?.maxAgeMs ?? DEFAULT_MAX_AGE_MS;\n\n const backups = await listBackups(libraryName);\n const now = Date.now();\n\n const backupsToDelete: string[] = [];\n\n for (let i = 0; i < backups.length; i++) {\n const backupPath = backups[i];\n if (!backupPath) continue;\n\n const stats = await stat(backupPath);\n const age = now - stats.mtimeMs;\n\n if (i >= maxGenerations || age > maxAgeMs) {\n backupsToDelete.push(backupPath);\n }\n }\n\n await Promise.all(backupsToDelete.map((backup) => unlink(backup)));\n}\n","/**\n * 3-way merge implementation with Last-Write-Wins (LWW) strategy\n */\n\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport type {\n FieldConflict,\n ItemConflict,\n MergeOptions,\n MergeResult,\n MergeStatus,\n} from \"./types.js\";\n\n/**\n * Get UUID from item, with fallback to id if uuid is missing\n */\nfunction getItemUuid(item: CslItem): string {\n return item.custom?.uuid || item.id || \"unknown\";\n}\n\n/**\n * Get timestamp from item, with fallback to created_at\n */\nfunction getTimestamp(item: CslItem): string {\n return item.custom?.timestamp || item.custom?.created_at || \"1970-01-01T00:00:00.000Z\";\n}\n\n/**\n * Deep equality check for field values\n */\nfunction deepEqual(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\n/**\n * Resolve a field conflict using LWW or prefer option\n */\nfunction resolveFieldConflict(\n localValue: unknown,\n remoteValue: unknown,\n localTimestamp: string,\n remoteTimestamp: string,\n options?: MergeOptions\n): unknown {\n if (localTimestamp > remoteTimestamp) {\n return localValue;\n }\n if (remoteTimestamp > localTimestamp) {\n return remoteValue;\n }\n // Timestamps equal: use prefer option or default to local\n if (options?.prefer === \"remote\") {\n return remoteValue;\n }\n return localValue;\n}\n\n/**\n * Determine conflict resolution type\n */\nfunction determineResolution(\n fieldConflicts: FieldConflict[],\n localTimestamp: string,\n remoteTimestamp: string,\n options?: MergeOptions\n): ItemConflict[\"resolution\"] {\n const hasRealConflicts = fieldConflicts.every((fc) => fc.resolved !== undefined);\n const localIsNewer = fieldConflicts.some(\n (fc) => fc.local !== fc.remote && localTimestamp > remoteTimestamp\n );\n const remoteIsNewer = fieldConflicts.some(\n (fc) => fc.local !== fc.remote && remoteTimestamp > localTimestamp\n );\n\n if (hasRealConflicts && localIsNewer) return \"auto-lww\";\n if (hasRealConflicts && remoteIsNewer) return \"auto-lww\";\n if (options?.prefer === \"local\") return \"prefer-local\";\n if (options?.prefer === \"remote\") return \"prefer-remote\";\n return \"unresolved\";\n}\n\n/**\n * Merge a single field from base, local, and remote versions\n */\nfunction mergeField(\n key: string,\n baseValue: unknown,\n localValue: unknown,\n remoteValue: unknown,\n localTimestamp: string,\n remoteTimestamp: string,\n options?: MergeOptions\n): { value: unknown; conflict: FieldConflict | null } {\n const localChanged = !deepEqual(baseValue, localValue);\n const remoteChanged = !deepEqual(baseValue, remoteValue);\n\n if (!localChanged && !remoteChanged) {\n return { value: baseValue, conflict: null };\n }\n\n if (localChanged && !remoteChanged) {\n return { value: localValue, conflict: null };\n }\n\n if (!localChanged && remoteChanged) {\n return { value: remoteValue, conflict: null };\n }\n\n // Both changed\n if (deepEqual(localValue, remoteValue)) {\n return { value: localValue, conflict: null };\n }\n\n // Both changed to different values\n const resolved = resolveFieldConflict(\n localValue,\n remoteValue,\n localTimestamp,\n remoteTimestamp,\n options\n );\n\n // Don't record conflicts for 'custom' metadata field\n if (key === \"custom\") {\n return { value: resolved, conflict: null };\n }\n\n return {\n value: resolved,\n conflict: {\n field: key,\n base: baseValue,\n local: localValue,\n remote: remoteValue,\n resolved,\n },\n };\n}\n\n/**\n * Merge a single item from base, local, and remote versions\n */\nfunction mergeItem(\n base: CslItem,\n local: CslItem,\n remote: CslItem,\n options?: MergeOptions\n): { merged: CslItem; conflict: ItemConflict | null } {\n const uuid = getItemUuid(base);\n const localTimestamp = getTimestamp(local);\n const remoteTimestamp = getTimestamp(remote);\n\n const merged: CslItem = { ...base };\n const fieldConflicts: FieldConflict[] = [];\n\n // Get all unique keys from all three versions\n const allKeys = new Set<string>([\n ...Object.keys(base),\n ...Object.keys(local),\n ...Object.keys(remote),\n ]);\n\n for (const key of allKeys) {\n const baseValue = (base as Record<string, unknown>)[key];\n const localValue = (local as Record<string, unknown>)[key];\n const remoteValue = (remote as Record<string, unknown>)[key];\n\n const { value, conflict } = mergeField(\n key,\n baseValue,\n localValue,\n remoteValue,\n localTimestamp,\n remoteTimestamp,\n options\n );\n\n (merged as Record<string, unknown>)[key] = value;\n\n if (conflict) {\n fieldConflicts.push(conflict);\n }\n }\n\n // If there are field conflicts, create ItemConflict\n if (fieldConflicts.length > 0) {\n const resolution = determineResolution(\n fieldConflicts,\n localTimestamp,\n remoteTimestamp,\n options\n );\n\n return {\n merged,\n conflict: {\n uuid,\n id: base.id || \"unknown\",\n fields: fieldConflicts,\n localTimestamp,\n remoteTimestamp,\n resolution,\n },\n };\n }\n\n return { merged, conflict: null };\n}\n\n/**\n * Build UUID-indexed maps from item arrays\n */\nfunction buildItemMaps(base: CslItem[], local: CslItem[], remote: CslItem[]) {\n const baseMap = new Map<string, CslItem>();\n const localMap = new Map<string, CslItem>();\n const remoteMap = new Map<string, CslItem>();\n\n for (const item of base) {\n baseMap.set(getItemUuid(item), item);\n }\n for (const item of local) {\n localMap.set(getItemUuid(item), item);\n }\n for (const item of remote) {\n remoteMap.set(getItemUuid(item), item);\n }\n\n return { baseMap, localMap, remoteMap };\n}\n\n/**\n * Handle items that exist in all three versions\n */\nfunction mergeExistingItem(\n baseItem: CslItem,\n localItem: CslItem,\n remoteItem: CslItem,\n options: MergeOptions | undefined,\n merged: CslItem[],\n conflicts: ItemConflict[]\n): void {\n const { merged: mergedItem, conflict } = mergeItem(baseItem, localItem, remoteItem, options);\n merged.push(mergedItem);\n if (conflict) {\n conflicts.push(conflict);\n }\n}\n\n/**\n * Handle items added in both local and remote\n */\nfunction handleDualAddition(\n uuid: string,\n localItem: CslItem,\n remoteItem: CslItem,\n options: MergeOptions | undefined,\n merged: CslItem[],\n conflicts: ItemConflict[]\n): void {\n if (deepEqual(localItem, remoteItem)) {\n merged.push(localItem);\n } else {\n const syntheticBase: CslItem = {\n id: uuid,\n type: \"article\",\n custom: {\n uuid,\n created_at: \"1970-01-01T00:00:00.000Z\",\n timestamp: \"1970-01-01T00:00:00.000Z\",\n },\n };\n const { merged: mergedItem, conflict } = mergeItem(\n syntheticBase,\n localItem,\n remoteItem,\n options\n );\n merged.push(mergedItem);\n if (conflict) {\n conflicts.push(conflict);\n }\n }\n}\n\n/**\n * Process a single UUID across all three versions\n */\nfunction processItem(\n uuid: string,\n baseMap: Map<string, CslItem>,\n localMap: Map<string, CslItem>,\n remoteMap: Map<string, CslItem>,\n options: MergeOptions | undefined,\n result: {\n merged: CslItem[];\n conflicts: ItemConflict[];\n localOnly: CslItem[];\n remoteOnly: CslItem[];\n deletedInLocal: CslItem[];\n deletedInRemote: CslItem[];\n }\n): void {\n const baseItem = baseMap.get(uuid);\n const localItem = localMap.get(uuid);\n const remoteItem = remoteMap.get(uuid);\n\n if (baseItem && localItem && remoteItem) {\n mergeExistingItem(baseItem, localItem, remoteItem, options, result.merged, result.conflicts);\n } else if (!baseItem && localItem && remoteItem) {\n handleDualAddition(uuid, localItem, remoteItem, options, result.merged, result.conflicts);\n } else if (!baseItem && localItem && !remoteItem) {\n result.merged.push(localItem);\n result.localOnly.push(localItem);\n } else if (!baseItem && !localItem && remoteItem) {\n result.merged.push(remoteItem);\n result.remoteOnly.push(remoteItem);\n } else if (baseItem && !localItem && remoteItem) {\n result.deletedInLocal.push(baseItem);\n } else if (baseItem && localItem && !remoteItem) {\n result.deletedInRemote.push(baseItem);\n } else if (baseItem && !localItem && !remoteItem) {\n result.deletedInLocal.push(baseItem);\n result.deletedInRemote.push(baseItem);\n }\n}\n\n/**\n * Performs a 3-way merge of CSL-JSON items\n *\n * @param base - Base version (common ancestor)\n * @param local - Local version (current working copy)\n * @param remote - Remote version (incoming changes)\n * @param options - Merge options (e.g., prefer local/remote for tie-breaking)\n * @returns Merge result with merged items and conflict information\n */\nexport function threeWayMerge(\n base: CslItem[],\n local: CslItem[],\n remote: CslItem[],\n options?: MergeOptions\n): MergeResult {\n const { baseMap, localMap, remoteMap } = buildItemMaps(base, local, remote);\n\n const result = {\n merged: [] as CslItem[],\n conflicts: [] as ItemConflict[],\n localOnly: [] as CslItem[],\n remoteOnly: [] as CslItem[],\n deletedInLocal: [] as CslItem[],\n deletedInRemote: [] as CslItem[],\n };\n\n const allUuids = new Set<string>([...baseMap.keys(), ...localMap.keys(), ...remoteMap.keys()]);\n\n for (const uuid of allUuids) {\n processItem(uuid, baseMap, localMap, remoteMap, options, result);\n }\n\n // Determine overall status\n let status: MergeStatus = \"success\";\n if (result.conflicts.length > 0) {\n const hasUnresolved = result.conflicts.some((c) => c.resolution === \"unresolved\");\n status = hasUnresolved ? \"conflict\" : \"auto-resolved\";\n }\n\n return {\n status,\n ...result,\n };\n}\n"],"names":["i","f"],"mappings":"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"}
@@ -1,12 +1,12 @@
1
1
  import type { Config } from "../config/schema.js";
2
- import { Library } from "../core/library.js";
3
2
  import { FileWatcher } from "../features/file-watcher/file-watcher.js";
3
+ import type { ILibraryOperations } from "../features/operations/library-operations.js";
4
4
  /**
5
- * MCP context containing library, config, and file watcher.
6
- * Simplified context for MCP server (no ExecutionContext pattern).
5
+ * MCP context containing libraryOperations, config, and file watcher.
6
+ * Uses ILibraryOperations pattern for consistency with CLI (see ADR-009, ADR-010).
7
7
  */
8
8
  export interface McpContext {
9
- library: Library;
9
+ libraryOperations: ILibraryOperations;
10
10
  config: Config;
11
11
  fileWatcher: FileWatcher;
12
12
  dispose: () => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/mcp/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,0CAA0C,CAAC;AAEvE;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,UAAU,CAAC,CAyC5F"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/mcp/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,MAAM,0CAA0C,CAAC;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAC;AAGvF;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,iBAAiB,EAAE,kBAAkB,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,UAAU,CAAC,CA6C5F"}
@@ -1,10 +1,10 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import type { Library } from "../../core/library.js";
2
+ import type { ILibraryOperations } from "../../features/operations/library-operations.js";
3
3
  /**
4
4
  * Register all resources with the MCP server.
5
5
  *
6
6
  * @param server - The MCP server instance
7
- * @param getLibrary - Function to get the current library instance
7
+ * @param getLibraryOperations - Function to get the current library operations instance
8
8
  */
9
- export declare function registerAllResources(server: McpServer, getLibrary: () => Library): void;
9
+ export declare function registerAllResources(server: McpServer, getLibraryOperations: () => ILibraryOperations): void;
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mcp/resources/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAOrD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,OAAO,GAAG,IAAI,CAIvF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mcp/resources/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAO1F;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,oBAAoB,EAAE,MAAM,kBAAkB,GAC7C,IAAI,CAIN"}
@@ -1,21 +1,21 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import type { Library } from "../../core/library.js";
2
+ import type { ILibraryOperations } from "../../features/operations/library-operations.js";
3
3
  /**
4
4
  * Register the references resource with the MCP server.
5
5
  * Returns all references as CSL-JSON.
6
6
  *
7
7
  * @param server - The MCP server instance
8
- * @param getLibrary - Function to get the current library instance
8
+ * @param getLibraryOperations - Function to get the current library operations instance
9
9
  */
10
- export declare function registerReferencesResource(server: McpServer, getLibrary: () => Library): void;
10
+ export declare function registerReferencesResource(server: McpServer, getLibraryOperations: () => ILibraryOperations): void;
11
11
  /**
12
12
  * Register the single reference resource with the MCP server.
13
13
  * Returns a single reference by ID.
14
14
  *
15
15
  * @param server - The MCP server instance
16
- * @param getLibrary - Function to get the current library instance
16
+ * @param getLibraryOperations - Function to get the current library operations instance
17
17
  */
18
- export declare function registerReferenceResource(server: McpServer, getLibrary: () => Library): void;
18
+ export declare function registerReferenceResource(server: McpServer, getLibraryOperations: () => ILibraryOperations): void;
19
19
  /**
20
20
  * Register the styles resource with the MCP server.
21
21
  * Returns available citation styles.
@@ -1 +1 @@
1
- {"version":3,"file":"library.d.ts","sourceRoot":"","sources":["../../../src/mcp/resources/library.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,OAAO,GAAG,IAAI,CAwB7F;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,OAAO,GAAG,IAAI,CA4C5F;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAyB9D"}
1
+ {"version":3,"file":"library.d.ts","sourceRoot":"","sources":["../../../src/mcp/resources/library.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAE1F;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,oBAAoB,EAAE,MAAM,kBAAkB,GAC7C,IAAI,CAuBN;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS,EACjB,oBAAoB,EAAE,MAAM,kBAAkB,GAC7C,IAAI,CA0CN;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAyB9D"}
@@ -1,5 +1,5 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import type { Library } from "../../core/library.js";
2
+ import type { ILibraryOperations } from "../../features/operations/library-operations.js";
3
3
  /**
4
4
  * Parameters for the add tool
5
5
  */
@@ -11,7 +11,7 @@ export interface AddToolParams {
11
11
  * Register the add tool with the MCP server.
12
12
  *
13
13
  * @param server - The MCP server instance
14
- * @param getLibrary - Function to get the current library instance
14
+ * @param getLibraryOperations - Function to get the current library operations instance
15
15
  */
16
- export declare function registerAddTool(server: McpServer, getLibrary: () => Library): void;
16
+ export declare function registerAddTool(server: McpServer, getLibraryOperations: () => ILibraryOperations): void;
17
17
  //# sourceMappingURL=add.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/add.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAqCD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,OAAO,GAAG,IAAI,CAkClF"}
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/add.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAE1F;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAqCD;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,SAAS,EACjB,oBAAoB,EAAE,MAAM,kBAAkB,GAC7C,IAAI,CAkCN"}
@@ -1,5 +1,5 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import type { Library } from "../../core/library.js";
2
+ import type { ILibraryOperations } from "../../features/operations/library-operations.js";
3
3
  export interface CiteToolParams {
4
4
  ids: string[];
5
5
  style?: string | undefined;
@@ -9,7 +9,7 @@ export interface CiteToolParams {
9
9
  * Register the cite tool with the MCP server.
10
10
  *
11
11
  * @param server - The MCP server instance
12
- * @param getLibrary - Function to get the current library instance
12
+ * @param getLibraryOperations - Function to get the current library operations instance
13
13
  */
14
- export declare function registerCiteTool(server: McpServer, getLibrary: () => Library): void;
14
+ export declare function registerCiteTool(server: McpServer, getLibraryOperations: () => ILibraryOperations): void;
15
15
  //# sourceMappingURL=cite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cite.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/cite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,OAAO,GAAG,IAAI,CAkCnF"}
1
+ {"version":3,"file":"cite.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/cite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAE1F,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,oBAAoB,EAAE,MAAM,kBAAkB,GAC7C,IAAI,CAkCN"}
@@ -1,6 +1,6 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import type { Config } from "../../config/schema.js";
3
- import type { Library } from "../../core/library.js";
3
+ import type { ILibraryOperations } from "../../features/operations/library-operations.js";
4
4
  /**
5
5
  * Parameters for the fulltext_attach tool
6
6
  */
@@ -28,24 +28,24 @@ export interface FulltextDetachToolParams {
28
28
  * Register the fulltext_attach tool with the MCP server.
29
29
  *
30
30
  * @param server - The MCP server instance
31
- * @param getLibrary - Function to get the current library instance
31
+ * @param getLibraryOperations - Function to get the current library operations instance
32
32
  * @param getConfig - Function to get the current config
33
33
  */
34
- export declare function registerFulltextAttachTool(server: McpServer, getLibrary: () => Library, getConfig: () => Config): void;
34
+ export declare function registerFulltextAttachTool(server: McpServer, getLibraryOperations: () => ILibraryOperations, getConfig: () => Config): void;
35
35
  /**
36
36
  * Register the fulltext_get tool with the MCP server.
37
37
  *
38
38
  * @param server - The MCP server instance
39
- * @param getLibrary - Function to get the current library instance
39
+ * @param getLibraryOperations - Function to get the current library operations instance
40
40
  * @param getConfig - Function to get the current config
41
41
  */
42
- export declare function registerFulltextGetTool(server: McpServer, getLibrary: () => Library, getConfig: () => Config): void;
42
+ export declare function registerFulltextGetTool(server: McpServer, getLibraryOperations: () => ILibraryOperations, getConfig: () => Config): void;
43
43
  /**
44
44
  * Register the fulltext_detach tool with the MCP server.
45
45
  *
46
46
  * @param server - The MCP server instance
47
- * @param getLibrary - Function to get the current library instance
47
+ * @param getLibraryOperations - Function to get the current library operations instance
48
48
  * @param getConfig - Function to get the current config
49
49
  */
50
- export declare function registerFulltextDetachTool(server: McpServer, getLibrary: () => Library, getConfig: () => Config): void;
50
+ export declare function registerFulltextDetachTool(server: McpServer, getLibraryOperations: () => ILibraryOperations, getConfig: () => Config): void;
51
51
  //# sourceMappingURL=fulltext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fulltext.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/fulltext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAOrD;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,mBAAmB;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,mBAAmB;IACnB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,mBAAmB;IACnB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,OAAO,EACzB,SAAS,EAAE,MAAM,MAAM,GACtB,IAAI,CAuCN;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,OAAO,EACzB,SAAS,EAAE,MAAM,MAAM,GACtB,IAAI,CAgEN;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,OAAO,EACzB,SAAS,EAAE,MAAM,MAAM,GACtB,IAAI,CAqCN"}
1
+ {"version":3,"file":"fulltext.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/fulltext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAMrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAE1F;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,mBAAmB;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,mBAAmB;IACnB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,mBAAmB;IACnB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,oBAAoB,EAAE,MAAM,kBAAkB,EAC9C,SAAS,EAAE,MAAM,MAAM,GACtB,IAAI,CAuCN;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,oBAAoB,EAAE,MAAM,kBAAkB,EAC9C,SAAS,EAAE,MAAM,MAAM,GACtB,IAAI,CAgEN;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,oBAAoB,EAAE,MAAM,kBAAkB,EAC9C,SAAS,EAAE,MAAM,MAAM,GACtB,IAAI,CAqCN"}
@@ -1,12 +1,12 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import type { Config } from "../../config/schema.js";
3
- import type { Library } from "../../core/library.js";
3
+ import type { ILibraryOperations } from "../../features/operations/library-operations.js";
4
4
  /**
5
5
  * Register all tools with the MCP server.
6
6
  *
7
7
  * @param server - The MCP server instance
8
- * @param getLibrary - Function to get the current library instance
8
+ * @param getLibraryOperations - Function to get the current library operations instance
9
9
  * @param getConfig - Function to get the current config (required for fulltext tools)
10
10
  */
11
- export declare function registerAllTools(server: McpServer, getLibrary: () => Library, getConfig: () => Config): void;
11
+ export declare function registerAllTools(server: McpServer, getLibraryOperations: () => ILibraryOperations, getConfig: () => Config): void;
12
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAYrD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,OAAO,EACzB,SAAS,EAAE,MAAM,MAAM,GACtB,IAAI,CASN"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAY1F;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,oBAAoB,EAAE,MAAM,kBAAkB,EAC9C,SAAS,EAAE,MAAM,MAAM,GACtB,IAAI,CASN"}
@@ -1,13 +1,20 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import type { Library } from "../../core/library.js";
2
+ import type { Config } from "../../config/schema.js";
3
+ import type { ILibraryOperations } from "../../features/operations/library-operations.js";
4
+ import { type SortField, type SortOrder } from "../../features/pagination/index.js";
3
5
  export interface ListToolParams {
4
6
  format?: "json" | "bibtex" | "pretty" | undefined;
7
+ sort?: SortField | undefined;
8
+ order?: SortOrder | undefined;
9
+ limit?: number | undefined;
10
+ offset?: number | undefined;
5
11
  }
6
12
  /**
7
13
  * Register the list tool with the MCP server.
8
14
  *
9
15
  * @param server - The MCP server instance
10
- * @param getLibrary - Function to get the current library instance
16
+ * @param getLibraryOperations - Function to get the current library operations instance
17
+ * @param getConfig - Function to get the current config
11
18
  */
12
- export declare function registerListTool(server: McpServer, getLibrary: () => Library): void;
19
+ export declare function registerListTool(server: McpServer, getLibraryOperations: () => ILibraryOperations, getConfig: () => Config): void;
13
20
  //# sourceMappingURL=list.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;CACnD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,OAAO,GAAG,IAAI,CA0BnF"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAC1F,OAAO,EACL,KAAK,SAAS,EACd,KAAK,SAAS,EAGf,MAAM,oCAAoC,CAAC;AAG5C,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IAClD,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,oBAAoB,EAAE,MAAM,kBAAkB,EAC9C,SAAS,EAAE,MAAM,MAAM,GACtB,IAAI,CA0DN"}
@@ -1,5 +1,5 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import type { Library } from "../../core/library.js";
2
+ import type { ILibraryOperations } from "../../features/operations/library-operations.js";
3
3
  /**
4
4
  * Parameters for the remove tool
5
5
  */
@@ -13,7 +13,7 @@ export interface RemoveToolParams {
13
13
  * Register the remove tool with the MCP server.
14
14
  *
15
15
  * @param server - The MCP server instance
16
- * @param getLibrary - Function to get the current library instance
16
+ * @param getLibraryOperations - Function to get the current library operations instance
17
17
  */
18
- export declare function registerRemoveTool(server: McpServer, getLibrary: () => Library): void;
18
+ export declare function registerRemoveTool(server: McpServer, getLibraryOperations: () => ILibraryOperations): void;
19
19
  //# sourceMappingURL=remove.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/remove.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,uDAAuD;IACvD,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,OAAO,GAAG,IAAI,CAiDrF"}
1
+ {"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/remove.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAE1F;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,uDAAuD;IACvD,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,oBAAoB,EAAE,MAAM,kBAAkB,GAC7C,IAAI,CAiDN"}
@@ -1,13 +1,20 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import type { Library } from "../../core/library.js";
2
+ import type { Config } from "../../config/schema.js";
3
+ import type { ILibraryOperations } from "../../features/operations/library-operations.js";
4
+ import { type SearchSortField, type SortOrder } from "../../features/pagination/index.js";
3
5
  export interface SearchToolParams {
4
6
  query: string;
7
+ sort?: SearchSortField | undefined;
8
+ order?: SortOrder | undefined;
9
+ limit?: number | undefined;
10
+ offset?: number | undefined;
5
11
  }
6
12
  /**
7
13
  * Register the search tool with the MCP server.
8
14
  *
9
15
  * @param server - The MCP server instance
10
- * @param getLibrary - Function to get the current library instance
16
+ * @param getLibraryOperations - Function to get the current library operations instance
17
+ * @param getConfig - Function to get the current config
11
18
  */
12
- export declare function registerSearchTool(server: McpServer, getLibrary: () => Library): void;
19
+ export declare function registerSearchTool(server: McpServer, getLibraryOperations: () => ILibraryOperations, getConfig: () => Config): void;
13
20
  //# sourceMappingURL=search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,OAAO,GAAG,IAAI,CAyBrF"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAC1F,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,SAAS,EAGf,MAAM,oCAAoC,CAAC;AAG5C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACnC,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,oBAAoB,EAAE,MAAM,kBAAkB,EAC9C,SAAS,EAAE,MAAM,MAAM,GACtB,IAAI,CA0DN"}
@@ -12,6 +12,19 @@ declare const listRequestBodySchema: z.ZodObject<{
12
12
  pretty: "pretty";
13
13
  "ids-only": "ids-only";
14
14
  }>>;
15
+ sort: z.ZodOptional<z.ZodEnum<{
16
+ title: "title";
17
+ author: "author";
18
+ updated: "updated";
19
+ created: "created";
20
+ published: "published";
21
+ }>>;
22
+ order: z.ZodOptional<z.ZodEnum<{
23
+ asc: "asc";
24
+ desc: "desc";
25
+ }>>;
26
+ limit: z.ZodOptional<z.ZodNumber>;
27
+ offset: z.ZodOptional<z.ZodNumber>;
15
28
  }, z.core.$strip>;
16
29
  /**
17
30
  * Request body type for list endpoint
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/server/routes/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD;;GAEG;AACH,QAAA,MAAM,qBAAqB;;;;;;;;iBAEzB,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,8EAkC/C"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/server/routes/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAKrD;;GAEG;AACH,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;iBAMzB,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,8EAsC/C"}
@@ -13,6 +13,20 @@ declare const searchRequestBodySchema: z.ZodObject<{
13
13
  pretty: "pretty";
14
14
  "ids-only": "ids-only";
15
15
  }>>;
16
+ sort: z.ZodOptional<z.ZodEnum<{
17
+ title: "title";
18
+ author: "author";
19
+ updated: "updated";
20
+ created: "created";
21
+ published: "published";
22
+ relevance: "relevance";
23
+ }>>;
24
+ order: z.ZodOptional<z.ZodEnum<{
25
+ asc: "asc";
26
+ desc: "desc";
27
+ }>>;
28
+ limit: z.ZodOptional<z.ZodNumber>;
29
+ offset: z.ZodOptional<z.ZodNumber>;
16
30
  }, z.core.$strip>;
17
31
  /**
18
32
  * Request body type for search endpoint
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/server/routes/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD;;GAEG;AACH,QAAA,MAAM,uBAAuB;;;;;;;;;iBAG3B,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,8EAoCjD"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/server/routes/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAKrD;;GAEG;AACH,QAAA,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;iBAO3B,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,8EAmCjD"}
package/dist/server.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import "hono";
2
- import "./chunks/file-watcher-CBAbblss.js";
3
- import { d, b } from "./chunks/index-Bl_mOQRe.js";
2
+ import "./chunks/file-watcher-D7oyc-9z.js";
3
+ import { c, s } from "./chunks/index-_7NEUoS7.js";
4
4
  export {
5
- d as createServer,
6
- b as startServerWithFileWatcher
5
+ c as createServer,
6
+ s as startServerWithFileWatcher
7
7
  };
8
8
  //# sourceMappingURL=server.js.map
@@ -2,4 +2,5 @@ export { createLogger, type Logger, type LogLevel } from "./logger";
2
2
  export { computeHash, computeFileHash } from "./hash";
3
3
  export { writeFileAtomic, ensureDirectoryExists } from "./file";
4
4
  export { createBackup, cleanupOldBackups, getBackupDirectory, listBackups, type BackupOptions, } from "./backup";
5
+ export { pickDefined } from "./object";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAChE,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,KAAK,aAAa,GACnB,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAChE,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,KAAK,aAAa,GACnB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Object utility functions
3
+ */
4
+ /**
5
+ * Pick defined (non-undefined) properties from an object for specified keys.
6
+ * Useful for building options objects where undefined values should be omitted
7
+ * (especially with exactOptionalPropertyTypes).
8
+ *
9
+ * @param obj - Source object
10
+ * @param keys - Keys to pick from the object
11
+ * @returns New object with only defined values for specified keys
12
+ */
13
+ export declare function pickDefined<T, K extends keyof T>(obj: T, keys: readonly K[]): {
14
+ [P in K]?: Exclude<T[P], undefined>;
15
+ };
16
+ //# sourceMappingURL=object.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object.d.ts","sourceRoot":"","sources":["../../src/utils/object.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EAC9C,GAAG,EAAE,CAAC,EACN,IAAI,EAAE,SAAS,CAAC,EAAE,GACjB;KAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC;CAAE,CAQzC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ncukondo/reference-manager",
3
- "version": "0.5.3",
3
+ "version": "0.7.0",
4
4
  "description": "A local reference management tool using CSL-JSON as the single source of truth",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -53,7 +53,9 @@
53
53
  "@modelcontextprotocol/sdk": "^1.25.1",
54
54
  "chokidar": "^5.0.0",
55
55
  "commander": "^12.1.0",
56
+ "enquirer": "^2.4.1",
56
57
  "hono": "^4.11.1",
58
+ "tabtab": "^3.0.2",
57
59
  "write-file-atomic": "^7.0.0",
58
60
  "zod": "^4.1.13"
59
61
  },
@@ -62,6 +64,7 @@
62
64
  "@hono/node-server": "^1.19.7",
63
65
  "@types/chokidar": "^1.7.5",
64
66
  "@types/node": "^22.19.2",
67
+ "@types/tabtab": "^3.0.4",
65
68
  "@types/write-file-atomic": "^4.0.3",
66
69
  "@vitest/coverage-v8": "^2.1.9",
67
70
  "typescript": "^5.6.0",