@ncukondo/reference-manager 0.1.0 → 0.3.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 (116) hide show
  1. package/README.md +40 -0
  2. package/dist/chunks/detector-DHztTaFY.js +619 -0
  3. package/dist/chunks/detector-DHztTaFY.js.map +1 -0
  4. package/dist/chunks/{detector-BF8Mcc72.js → loader-mQ25o6cV.js} +303 -664
  5. package/dist/chunks/loader-mQ25o6cV.js.map +1 -0
  6. package/dist/chunks/search-Be9vzUIH.js +29541 -0
  7. package/dist/chunks/search-Be9vzUIH.js.map +1 -0
  8. package/dist/cli/commands/add.d.ts +44 -16
  9. package/dist/cli/commands/add.d.ts.map +1 -1
  10. package/dist/cli/commands/cite.d.ts +49 -0
  11. package/dist/cli/commands/cite.d.ts.map +1 -0
  12. package/dist/cli/commands/fulltext.d.ts +101 -0
  13. package/dist/cli/commands/fulltext.d.ts.map +1 -0
  14. package/dist/cli/commands/index.d.ts +14 -10
  15. package/dist/cli/commands/index.d.ts.map +1 -1
  16. package/dist/cli/commands/list.d.ts +23 -6
  17. package/dist/cli/commands/list.d.ts.map +1 -1
  18. package/dist/cli/commands/remove.d.ts +47 -12
  19. package/dist/cli/commands/remove.d.ts.map +1 -1
  20. package/dist/cli/commands/search.d.ts +24 -7
  21. package/dist/cli/commands/search.d.ts.map +1 -1
  22. package/dist/cli/commands/update.d.ts +26 -13
  23. package/dist/cli/commands/update.d.ts.map +1 -1
  24. package/dist/cli/execution-context.d.ts +60 -0
  25. package/dist/cli/execution-context.d.ts.map +1 -0
  26. package/dist/cli/helpers.d.ts +18 -0
  27. package/dist/cli/helpers.d.ts.map +1 -1
  28. package/dist/cli/index.d.ts.map +1 -1
  29. package/dist/cli/server-client.d.ts +73 -10
  30. package/dist/cli/server-client.d.ts.map +1 -1
  31. package/dist/cli.js +1200 -528
  32. package/dist/cli.js.map +1 -1
  33. package/dist/config/csl-styles.d.ts +83 -0
  34. package/dist/config/csl-styles.d.ts.map +1 -0
  35. package/dist/config/defaults.d.ts +10 -0
  36. package/dist/config/defaults.d.ts.map +1 -1
  37. package/dist/config/loader.d.ts.map +1 -1
  38. package/dist/config/schema.d.ts +84 -0
  39. package/dist/config/schema.d.ts.map +1 -1
  40. package/dist/core/csl-json/types.d.ts +15 -3
  41. package/dist/core/csl-json/types.d.ts.map +1 -1
  42. package/dist/core/library.d.ts +60 -0
  43. package/dist/core/library.d.ts.map +1 -1
  44. package/dist/features/format/bibtex.d.ts +6 -0
  45. package/dist/features/format/bibtex.d.ts.map +1 -0
  46. package/dist/features/format/citation-csl.d.ts +41 -0
  47. package/dist/features/format/citation-csl.d.ts.map +1 -0
  48. package/dist/features/format/citation-fallback.d.ts +24 -0
  49. package/dist/features/format/citation-fallback.d.ts.map +1 -0
  50. package/dist/features/format/index.d.ts +10 -0
  51. package/dist/features/format/index.d.ts.map +1 -0
  52. package/dist/features/format/json.d.ts +6 -0
  53. package/dist/features/format/json.d.ts.map +1 -0
  54. package/dist/features/format/pretty.d.ts +6 -0
  55. package/dist/features/format/pretty.d.ts.map +1 -0
  56. package/dist/features/fulltext/filename.d.ts +17 -0
  57. package/dist/features/fulltext/filename.d.ts.map +1 -0
  58. package/dist/features/fulltext/index.d.ts +7 -0
  59. package/dist/features/fulltext/index.d.ts.map +1 -0
  60. package/dist/features/fulltext/manager.d.ts +109 -0
  61. package/dist/features/fulltext/manager.d.ts.map +1 -0
  62. package/dist/features/fulltext/types.d.ts +12 -0
  63. package/dist/features/fulltext/types.d.ts.map +1 -0
  64. package/dist/features/import/cache.d.ts +37 -0
  65. package/dist/features/import/cache.d.ts.map +1 -0
  66. package/dist/features/import/detector.d.ts +42 -0
  67. package/dist/features/import/detector.d.ts.map +1 -0
  68. package/dist/features/import/fetcher.d.ts +49 -0
  69. package/dist/features/import/fetcher.d.ts.map +1 -0
  70. package/dist/features/import/importer.d.ts +61 -0
  71. package/dist/features/import/importer.d.ts.map +1 -0
  72. package/dist/features/import/index.d.ts +8 -0
  73. package/dist/features/import/index.d.ts.map +1 -0
  74. package/dist/features/import/normalizer.d.ts +15 -0
  75. package/dist/features/import/normalizer.d.ts.map +1 -0
  76. package/dist/features/import/parser.d.ts +33 -0
  77. package/dist/features/import/parser.d.ts.map +1 -0
  78. package/dist/features/import/rate-limiter.d.ts +45 -0
  79. package/dist/features/import/rate-limiter.d.ts.map +1 -0
  80. package/dist/features/operations/add.d.ts +65 -0
  81. package/dist/features/operations/add.d.ts.map +1 -0
  82. package/dist/features/operations/cite.d.ts +48 -0
  83. package/dist/features/operations/cite.d.ts.map +1 -0
  84. package/dist/features/operations/list.d.ts +28 -0
  85. package/dist/features/operations/list.d.ts.map +1 -0
  86. package/dist/features/operations/remove.d.ts +29 -0
  87. package/dist/features/operations/remove.d.ts.map +1 -0
  88. package/dist/features/operations/search.d.ts +30 -0
  89. package/dist/features/operations/search.d.ts.map +1 -0
  90. package/dist/features/operations/update.d.ts +39 -0
  91. package/dist/features/operations/update.d.ts.map +1 -0
  92. package/dist/index.js +18 -16
  93. package/dist/index.js.map +1 -1
  94. package/dist/server/index.d.ts +3 -1
  95. package/dist/server/index.d.ts.map +1 -1
  96. package/dist/server/routes/add.d.ts +11 -0
  97. package/dist/server/routes/add.d.ts.map +1 -0
  98. package/dist/server/routes/cite.d.ts +9 -0
  99. package/dist/server/routes/cite.d.ts.map +1 -0
  100. package/dist/server/routes/list.d.ts +25 -0
  101. package/dist/server/routes/list.d.ts.map +1 -0
  102. package/dist/server/routes/references.d.ts.map +1 -1
  103. package/dist/server/routes/search.d.ts +26 -0
  104. package/dist/server/routes/search.d.ts.map +1 -0
  105. package/dist/server.js +215 -32
  106. package/dist/server.js.map +1 -1
  107. package/package.json +15 -4
  108. package/dist/chunks/detector-BF8Mcc72.js.map +0 -1
  109. package/dist/cli/output/bibtex.d.ts +0 -6
  110. package/dist/cli/output/bibtex.d.ts.map +0 -1
  111. package/dist/cli/output/index.d.ts +0 -7
  112. package/dist/cli/output/index.d.ts.map +0 -1
  113. package/dist/cli/output/json.d.ts +0 -6
  114. package/dist/cli/output/json.d.ts.map +0 -1
  115. package/dist/cli/output/pretty.d.ts +0 -6
  116. package/dist/cli/output/pretty.d.ts.map +0 -1
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sources":["../src/server/portfile.ts","../src/cli/commands/add.ts","../src/cli/output/bibtex.ts","../src/cli/output/json.ts","../src/cli/output/pretty.ts","../src/cli/commands/list.ts","../src/cli/commands/search.ts","../src/cli/commands/server.ts","../src/cli/commands/update.ts","../src/cli/helpers.ts","../src/cli/server-client.ts","../src/cli/server-detection.ts","../src/cli/index.ts"],"sourcesContent":["import { promises as fs } from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\n\n/**\n * Get the default portfile path.\n * @returns The path to the portfile in the system's temp directory.\n */\nexport function getPortfilePath(): string {\n const tmpDir = os.tmpdir();\n return path.join(tmpDir, \"reference-manager\", \"server.port\");\n}\n\n/**\n * Write port, PID, library path, and optionally started_at to the portfile.\n * @param portfilePath - Path to the portfile\n * @param port - Server port number\n * @param pid - Server process ID\n * @param library - Path to the library file\n * @param started_at - Optional ISO 8601 timestamp of when the server started\n */\nexport async function writePortfile(\n portfilePath: string,\n port: number,\n pid: number,\n library: string,\n started_at?: string\n): Promise<void> {\n // Create parent directory if it doesn't exist\n const dir = path.dirname(portfilePath);\n await fs.mkdir(dir, { recursive: true });\n\n // Write portfile with port, pid, library, and optionally started_at\n const data: Record<string, unknown> = { port, pid, library };\n if (started_at !== undefined) {\n data.started_at = started_at;\n }\n const content = JSON.stringify(data, null, 2);\n await fs.writeFile(portfilePath, content, \"utf-8\");\n}\n\n/**\n * Read port, PID, library, and optionally started_at from the portfile.\n * @param portfilePath - Path to the portfile\n * @returns Object with port, pid, library (if present), and started_at (if present), or null if file doesn't exist or is invalid\n */\nexport async function readPortfile(portfilePath: string): Promise<{\n port: number;\n pid: number;\n library?: string;\n started_at?: string;\n} | null> {\n try {\n const content = await fs.readFile(portfilePath, \"utf-8\");\n const data = JSON.parse(content);\n\n // Validate required fields (port and pid are always required)\n if (typeof data.port !== \"number\" || typeof data.pid !== \"number\") {\n return null;\n }\n\n // Build result with required fields\n const result: {\n port: number;\n pid: number;\n library?: string;\n started_at?: string;\n } = {\n port: data.port,\n pid: data.pid,\n };\n\n // Add optional fields if present\n if (typeof data.library === \"string\") {\n result.library = data.library;\n }\n if (typeof data.started_at === \"string\") {\n result.started_at = data.started_at;\n }\n\n return result;\n } catch {\n // File doesn't exist or invalid JSON\n return null;\n }\n}\n\n/**\n * Check if the portfile exists.\n * @param portfilePath - Path to the portfile\n * @returns True if portfile exists, false otherwise\n */\nexport async function portfileExists(portfilePath: string): Promise<boolean> {\n try {\n await fs.access(portfilePath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove the portfile.\n * @param portfilePath - Path to the portfile\n */\nexport async function removePortfile(portfilePath: string): Promise<void> {\n try {\n await fs.unlink(portfilePath);\n } catch {\n // Ignore if file doesn't exist\n }\n}\n\n/**\n * Check if a process with the given PID is running.\n * @param pid - Process ID to check\n * @returns True if process is running, false otherwise\n */\nexport function isProcessRunning(pid: number): boolean {\n if (pid <= 0) {\n return false;\n }\n\n try {\n // Sending signal 0 checks if the process exists without killing it\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n","import type { CslItem } from \"../../core/csl-json/types.js\";\nimport { detectDuplicate } from \"../../features/duplicate/detector.js\";\nimport type { DuplicateMatch } from \"../../features/duplicate/types.js\";\n\nexport interface AddOptions {\n force: boolean;\n}\n\nexport interface AddResult {\n added: boolean;\n item: CslItem;\n idChanged: boolean;\n originalId?: string | undefined;\n duplicate?: DuplicateMatch | undefined;\n}\n\n/**\n * Generate suffix for ID collision (a, b, c, ..., z, aa, ab, ...)\n */\nfunction generateSuffix(index: number): string {\n const alphabet = \"abcdefghijklmnopqrstuvwxyz\";\n let suffix = \"\";\n let n = index;\n\n do {\n suffix = alphabet[n % 26] + suffix;\n n = Math.floor(n / 26) - 1;\n } while (n >= 0);\n\n return suffix;\n}\n\n/**\n * Resolve ID collision by appending suffix\n */\nfunction resolveIdCollision(baseId: string, existing: CslItem[]): { id: string; changed: boolean } {\n const existingIds = new Set(existing.map((item) => item.id));\n\n if (!existingIds.has(baseId)) {\n return { id: baseId, changed: false };\n }\n\n // Find next available suffix\n let index = 0;\n let newId: string;\n\n do {\n const suffix = generateSuffix(index);\n newId = `${baseId}${suffix}`;\n index++;\n } while (existingIds.has(newId));\n\n return { id: newId, changed: true };\n}\n\n/**\n * Add a new reference to the library.\n *\n * @param existing - Existing library items\n * @param newItem - New item to add\n * @param options - Add options\n * @returns Add result\n */\nexport async function add(\n existing: CslItem[],\n newItem: CslItem,\n options: AddOptions\n): Promise<AddResult> {\n // 1. Check for content duplicate (unless force=true)\n if (!options.force) {\n const duplicateResult = detectDuplicate(newItem, existing);\n\n if (duplicateResult.matches.length > 0) {\n // Duplicate found, reject\n return {\n added: false,\n item: newItem,\n idChanged: false,\n duplicate: duplicateResult.matches[0],\n };\n }\n }\n\n // 2. Resolve ID collision\n const originalId = newItem.id;\n const { id, changed } = resolveIdCollision(originalId, existing);\n\n const finalItem: CslItem = {\n ...newItem,\n id,\n };\n\n return {\n added: true,\n item: finalItem,\n idChanged: changed,\n originalId: changed ? originalId : undefined,\n };\n}\n","import type { CslItem } from \"../../core/csl-json/types\";\nimport type { Reference } from \"../../core/reference\";\n\n/**\n * Map CSL-JSON type to BibTeX entry type\n */\nfunction mapEntryType(cslType: string): string {\n const typeMap: Record<string, string> = {\n article: \"article\",\n \"article-journal\": \"article\",\n \"article-magazine\": \"article\",\n \"article-newspaper\": \"article\",\n book: \"book\",\n chapter: \"inbook\",\n \"paper-conference\": \"inproceedings\",\n thesis: \"phdthesis\",\n report: \"techreport\",\n webpage: \"misc\",\n };\n\n return typeMap[cslType] || \"misc\";\n}\n\n/**\n * Format a single author as \"Family, Given\"\n */\nfunction formatBibtexAuthor(author: {\n family?: string | undefined;\n given?: string | undefined;\n literal?: string | undefined;\n}): string {\n if (author.literal) {\n return author.literal;\n }\n const family = author.family || \"\";\n const given = author.given || \"\";\n return given ? `${family}, ${given}` : family;\n}\n\n/**\n * Format authors for BibTeX as \"Family1, Given1 and Family2, Given2\"\n */\nfunction formatBibtexAuthors(\n authors: Array<{\n family?: string | undefined;\n given?: string | undefined;\n literal?: string | undefined;\n \"dropping-particle\"?: string | undefined;\n \"non-dropping-particle\"?: string | undefined;\n suffix?: string | undefined;\n }>\n): string {\n return authors.map(formatBibtexAuthor).join(\" and \");\n}\n\n/**\n * Format a BibTeX field\n */\nfunction formatField(name: string, value: string): string {\n return ` ${name} = {${value}},`;\n}\n\n/**\n * Add basic bibliographic fields to BibTeX entry\n */\nfunction addBasicFields(lines: string[], item: CslItem): void {\n // Title\n if (item.title) {\n lines.push(formatField(\"title\", item.title));\n }\n\n // Authors\n if (item.author && item.author.length > 0) {\n lines.push(formatField(\"author\", formatBibtexAuthors(item.author)));\n }\n\n // Year\n const year = item.issued?.[\"date-parts\"]?.[0]?.[0];\n if (year) {\n lines.push(formatField(\"year\", String(year)));\n }\n}\n\n/**\n * Add publication details to BibTeX entry\n */\nfunction addPublicationDetails(lines: string[], item: CslItem, entryType: string): void {\n // Container-title (journal or booktitle depending on type)\n if (item[\"container-title\"]) {\n if (entryType === \"article\") {\n lines.push(formatField(\"journal\", item[\"container-title\"]));\n } else if (entryType === \"inbook\" || entryType === \"inproceedings\") {\n lines.push(formatField(\"booktitle\", item[\"container-title\"]));\n }\n }\n\n // Volume\n if (item.volume) {\n lines.push(formatField(\"volume\", item.volume));\n }\n\n // Issue -> number\n if (item.issue) {\n lines.push(formatField(\"number\", item.issue));\n }\n\n // Pages\n if (item.page) {\n lines.push(formatField(\"pages\", item.page));\n }\n\n // Publisher\n if (item.publisher) {\n lines.push(formatField(\"publisher\", item.publisher));\n }\n}\n\n/**\n * Add identifier fields to BibTeX entry\n */\nfunction addIdentifierFields(lines: string[], item: CslItem): void {\n // DOI\n if (item.DOI) {\n lines.push(formatField(\"doi\", item.DOI));\n }\n\n // URL\n if (item.URL) {\n lines.push(formatField(\"url\", item.URL));\n }\n\n // PMID or PMCID in note field\n if (item.PMID) {\n lines.push(formatField(\"note\", `PMID: ${item.PMID}`));\n } else if (item.PMCID) {\n lines.push(formatField(\"note\", `PMCID: ${item.PMCID}`));\n }\n}\n\n/**\n * Format a single reference as BibTeX entry\n */\nfunction formatSingleBibtexEntry(ref: Reference): string {\n const item: CslItem = ref.getItem();\n const entryType = mapEntryType(item.type);\n const lines: string[] = [];\n\n // Opening line: @type{citation-key,\n lines.push(`@${entryType}{${item.id},`);\n\n // Add fields in sections\n addBasicFields(lines, item);\n addPublicationDetails(lines, item, entryType);\n addIdentifierFields(lines, item);\n\n // Closing brace\n lines.push(\"}\");\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format references as BibTeX\n */\nexport function formatBibtex(references: Reference[]): string {\n if (references.length === 0) {\n return \"\";\n }\n\n return references.map(formatSingleBibtexEntry).join(\"\\n\\n\");\n}\n","import type { Reference } from \"../../core/reference\";\n\n/**\n * Format references as compact JSON\n */\nexport function formatJson(references: Reference[]): string {\n const items = references.map((ref) => ref.getItem());\n return JSON.stringify(items);\n}\n","import type { Reference } from \"../../core/reference\";\n\n/**\n * Format a single author as \"Family, Given-Initial.\"\n */\nfunction formatAuthor(author: {\n family?: string | undefined;\n given?: string | undefined;\n}): string {\n const family = author.family || \"\";\n const givenInitial = author.given ? `${author.given.charAt(0)}.` : \"\";\n return givenInitial ? `${family}, ${givenInitial}` : family;\n}\n\n/**\n * Format authors array as \"Family1, G.; Family2, G.\"\n */\nfunction formatAuthors(\n authors: Array<{\n family?: string | undefined;\n given?: string | undefined;\n literal?: string | undefined;\n \"dropping-particle\"?: string | undefined;\n \"non-dropping-particle\"?: string | undefined;\n suffix?: string | undefined;\n }>\n): string {\n return authors.map(formatAuthor).join(\"; \");\n}\n\n/**\n * Format a single reference in pretty format\n */\nfunction formatSingleReference(ref: Reference): string {\n const item = ref.getItem();\n const lines: string[] = [];\n\n // Header line: [id] title\n const header = item.title ? `[${item.id}] ${item.title}` : `[${item.id}]`;\n lines.push(header);\n\n // Authors (if present)\n if (item.author && item.author.length > 0) {\n lines.push(` Authors: ${formatAuthors(item.author)}`);\n }\n\n // Year\n const year = item.issued?.[\"date-parts\"]?.[0]?.[0];\n lines.push(` Year: ${year || \"(no year)\"}`);\n\n // Type\n lines.push(` Type: ${item.type}`);\n\n // DOI (if present)\n if (item.DOI) {\n lines.push(` DOI: ${item.DOI}`);\n }\n\n // PMID (if present)\n if (item.PMID) {\n lines.push(` PMID: ${item.PMID}`);\n }\n\n // PMCID (if present)\n if (item.PMCID) {\n lines.push(` PMCID: ${item.PMCID}`);\n }\n\n // URL (if present)\n if (item.URL) {\n lines.push(` URL: ${item.URL}`);\n }\n\n // UUID (always)\n lines.push(` UUID: ${ref.getUuid()}`);\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format references in pretty-printed format\n */\nexport function formatPretty(references: Reference[]): string {\n if (references.length === 0) {\n return \"\";\n }\n\n return references.map(formatSingleReference).join(\"\\n\\n\");\n}\n","import type { CslItem } from \"../../core/csl-json/types.js\";\nimport { Reference } from \"../../core/reference.js\";\nimport { formatBibtex } from \"../output/bibtex.js\";\nimport { formatJson } from \"../output/json.js\";\nimport { formatPretty } from \"../output/pretty.js\";\n\nexport interface ListOptions {\n json?: boolean;\n idsOnly?: boolean;\n uuid?: boolean;\n bibtex?: boolean;\n}\n\n/**\n * List all references in the library.\n *\n * @param items - Array of CSL items\n * @param options - Output format options\n */\nexport async function list(items: CslItem[], options: ListOptions): Promise<void> {\n // Check for conflicting output options\n const outputOptions = [options.json, options.idsOnly, options.uuid, options.bibtex].filter(\n Boolean\n );\n\n if (outputOptions.length > 1) {\n throw new Error(\n \"Multiple output formats specified. Only one of --json, --ids-only, --uuid, --bibtex can be used.\"\n );\n }\n\n // Convert CslItems to References for output formatters\n const references = items.map((item) => new Reference(item));\n\n // Output based on selected format\n if (options.json) {\n process.stdout.write(formatJson(references));\n } else if (options.idsOnly) {\n for (const item of items) {\n process.stdout.write(`${item.id}\\n`);\n }\n } else if (options.uuid) {\n for (const item of items) {\n if (item.custom) {\n process.stdout.write(`${item.custom.uuid}\\n`);\n }\n }\n } else if (options.bibtex) {\n process.stdout.write(formatBibtex(references));\n } else {\n // Default: pretty format\n process.stdout.write(formatPretty(references));\n }\n}\n","import type { CslItem } from \"../../core/csl-json/types.js\";\nimport { Reference } from \"../../core/reference.js\";\nimport { search as searchReferences } from \"../../features/search/matcher.js\";\nimport { sortResults } from \"../../features/search/sorter.js\";\nimport { tokenize } from \"../../features/search/tokenizer.js\";\nimport { formatBibtex } from \"../output/bibtex.js\";\nimport { formatJson } from \"../output/json.js\";\nimport { formatPretty } from \"../output/pretty.js\";\n\nexport interface SearchOptions {\n json?: boolean;\n idsOnly?: boolean;\n uuid?: boolean;\n bibtex?: boolean;\n}\n\n/**\n * Search references in the library.\n *\n * @param items - Array of CSL items\n * @param query - Search query string\n * @param options - Output format options\n */\nexport async function search(\n items: CslItem[],\n query: string,\n options: SearchOptions\n): Promise<void> {\n // Check for conflicting output options\n const outputOptions = [options.json, options.idsOnly, options.uuid, options.bibtex].filter(\n Boolean\n );\n\n if (outputOptions.length > 1) {\n throw new Error(\n \"Multiple output formats specified. Only one of --json, --ids-only, --uuid, --bibtex can be used.\"\n );\n }\n\n // Tokenize query\n const searchQuery = tokenize(query);\n\n // Search\n const results = searchReferences(items, searchQuery.tokens);\n\n // Sort results\n const sorted = sortResults(results);\n\n // Extract items from results\n const matchedItems = sorted.map((result) => result.reference);\n\n // Convert to References for output formatters\n const references = matchedItems.map((item) => new Reference(item));\n\n // Output based on selected format\n if (options.json) {\n process.stdout.write(formatJson(references));\n } else if (options.idsOnly) {\n for (const item of matchedItems) {\n process.stdout.write(`${item.id}\\n`);\n }\n } else if (options.uuid) {\n for (const item of matchedItems) {\n if (item.custom) {\n process.stdout.write(`${item.custom.uuid}\\n`);\n }\n }\n } else if (options.bibtex) {\n process.stdout.write(formatBibtex(references));\n } else {\n // Default: pretty format\n process.stdout.write(formatPretty(references));\n }\n}\n","import {\n isProcessRunning,\n readPortfile,\n removePortfile,\n writePortfile,\n} from \"../../server/portfile.js\";\n\nexport interface ServerStartOptions {\n port?: number;\n daemon?: boolean;\n library: string;\n portfilePath: string;\n}\n\nexport interface ServerInfo {\n port: number;\n pid: number;\n library: string;\n started_at?: string;\n}\n\n/**\n * Start HTTP server.\n *\n * @param options - Server start options\n */\nexport async function serverStart(options: ServerStartOptions): Promise<void> {\n // Check if server is already running\n const existingStatus = await serverStatus(options.portfilePath);\n if (existingStatus !== null) {\n throw new Error(\"Server is already running\");\n }\n\n // Determine port (use provided port or default to 3000 for testing)\n const port = options.port ?? 3000;\n\n // For daemon mode, create portfile with current process PID\n // (In real implementation, this would be the spawned server process PID)\n const pid = process.pid;\n const started_at = new Date().toISOString();\n\n await writePortfile(options.portfilePath, port, pid, options.library, started_at);\n\n // In real implementation, this would spawn the server process\n // For now, we just create the portfile for testing purposes\n}\n\n/**\n * Stop running server.\n *\n * @param portfilePath - Path to the portfile\n */\nexport async function serverStop(portfilePath: string): Promise<void> {\n // Check if server is running\n const status = await serverStatus(portfilePath);\n if (status === null) {\n throw new Error(\"Server is not running\");\n }\n\n // In real implementation, send SIGTERM to the server process\n // For testing, we just remove the portfile\n await removePortfile(portfilePath);\n\n process.stdout.write(\"Server stopped successfully\\n\");\n}\n\n/**\n * Get server status.\n *\n * @param portfilePath - Path to the portfile\n * @returns Server info if running, null otherwise\n */\nexport async function serverStatus(portfilePath: string): Promise<ServerInfo | null> {\n // Read portfile\n const portfileData = await readPortfile(portfilePath);\n if (portfileData === null) {\n return null;\n }\n\n // Check if process is still running\n if (!isProcessRunning(portfileData.pid)) {\n // Process not found, cleanup stale portfile\n await removePortfile(portfilePath);\n return null;\n }\n\n // Return server info\n const result: ServerInfo = {\n port: portfileData.port,\n pid: portfileData.pid,\n library: portfileData.library ?? \"\",\n };\n\n if (portfileData.started_at !== undefined) {\n result.started_at = portfileData.started_at;\n }\n\n return result;\n}\n","import type { CslItem } from \"../../core/csl-json/types.js\";\n\nexport interface UpdateOptions {\n byUuid: boolean;\n}\n\nexport interface UpdateResult {\n updated: boolean;\n item?: CslItem | undefined;\n items: CslItem[];\n}\n\n/**\n * Update a reference in the library.\n *\n * @param items - Library items\n * @param identifier - Reference ID or UUID\n * @param updates - Partial updates to apply\n * @param options - Update options\n * @returns Update result\n */\nexport async function update(\n items: CslItem[],\n identifier: string,\n updates: Partial<CslItem>,\n options: UpdateOptions\n): Promise<UpdateResult> {\n let foundIndex = -1;\n\n if (options.byUuid) {\n // Find by UUID\n foundIndex = items.findIndex((item) => item.custom?.uuid === identifier);\n } else {\n // Find by ID\n foundIndex = items.findIndex((item) => item.id === identifier);\n }\n\n if (foundIndex === -1) {\n // Not found\n return {\n updated: false,\n items,\n };\n }\n\n const existingItem = items[foundIndex];\n if (!existingItem) {\n // Should not happen, but TypeScript needs this\n return {\n updated: false,\n items,\n };\n }\n\n // Merge updates with existing item\n const updatedItem: CslItem = {\n ...existingItem,\n ...updates,\n // Ensure required fields\n id: updates.id ?? existingItem.id,\n type: updates.type ?? existingItem.type,\n // Preserve UUID and created_at\n custom: {\n ...(existingItem.custom || {}),\n ...(updates.custom || {}),\n uuid: existingItem.custom?.uuid || \"\",\n created_at: existingItem.custom?.created_at || new Date().toISOString(),\n // Update timestamp\n timestamp: new Date().toISOString(),\n },\n };\n\n const updatedItems = [...items.slice(0, foundIndex), updatedItem, ...items.slice(foundIndex + 1)];\n\n return {\n updated: true,\n item: updatedItem,\n items: updatedItems,\n };\n}\n","/**\n * CLI Helper Functions\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { stdin, stdout } from \"node:process\";\nimport { loadConfig } from \"../config/loader.js\";\nimport type { Config } from \"../config/schema.js\";\n\n/**\n * Output format type\n */\nexport type OutputFormat = \"pretty\" | \"json\" | \"ids-only\" | \"uuid\" | \"bibtex\";\n\n/**\n * CLI options interface\n */\nexport interface CliOptions {\n library?: string;\n logLevel?: \"silent\" | \"info\" | \"debug\";\n config?: string;\n quiet?: boolean;\n verbose?: boolean;\n backup?: boolean;\n backupDir?: string;\n json?: boolean;\n idsOnly?: boolean;\n uuid?: boolean;\n bibtex?: boolean;\n}\n\n/**\n * Read JSON input from file or stdin\n * @param file - File path (optional, defaults to stdin)\n * @returns JSON string\n */\nexport async function readJsonInput(file?: string): Promise<string> {\n if (file) {\n // Read from file\n try {\n return readFileSync(file, \"utf-8\");\n } catch (error) {\n throw new Error(\n `I/O error: Cannot read file ${file}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n // Read from stdin\n const chunks: Buffer[] = [];\n for await (const chunk of stdin) {\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks).toString(\"utf-8\");\n}\n\n/**\n * Parse JSON input\n * @param input - JSON string\n * @returns Parsed JSON object or array\n */\nexport function parseJsonInput(input: string): unknown {\n if (!input || input.trim() === \"\") {\n throw new Error(\"Parse error: Empty input\");\n }\n\n try {\n return JSON.parse(input);\n } catch (error) {\n throw new Error(\n `Parse error: Invalid JSON: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * Load config with CLI argument overrides\n * @param options - CLI options\n * @returns Config with overrides applied\n */\nexport async function loadConfigWithOverrides(options: CliOptions): Promise<Config> {\n // Load base config\n // Note: options.config is currently ignored as loadConfig uses cwd and userConfigPath\n const config = await loadConfig();\n\n // Apply CLI overrides\n const overrides: Partial<Config> = {};\n\n // Library path\n if (options.library) {\n overrides.library = options.library;\n }\n\n // Log level\n if (options.quiet) {\n overrides.logLevel = \"silent\";\n } else if (options.verbose) {\n overrides.logLevel = \"debug\";\n } else if (options.logLevel) {\n overrides.logLevel = options.logLevel;\n }\n\n // Backup settings\n if (options.backup !== undefined || options.backupDir) {\n overrides.backup = {\n ...config.backup,\n ...(options.backup !== undefined && { enabled: options.backup }),\n ...(options.backupDir && { directory: options.backupDir }),\n };\n }\n\n return { ...config, ...overrides };\n}\n\n/**\n * Get output format from options\n * @param options - CLI options\n * @returns Output format\n * @throws Error if multiple formats specified\n */\nexport function getOutputFormat(options: CliOptions): OutputFormat {\n const formats: OutputFormat[] = [];\n\n if (options.json) formats.push(\"json\");\n if (options.idsOnly) formats.push(\"ids-only\");\n if (options.uuid) formats.push(\"uuid\");\n if (options.bibtex) formats.push(\"bibtex\");\n\n if (formats.length > 1) {\n throw new Error(\n \"Multiple output formats specified. Only one of --json, --ids-only, --uuid, --bibtex can be used.\"\n );\n }\n\n return formats[0] || \"pretty\";\n}\n\n/**\n * Check if running in TTY\n * @returns True if stdin and stdout are TTY\n */\nexport function isTTY(): boolean {\n return Boolean(stdin.isTTY && stdout.isTTY);\n}\n\n/**\n * Read confirmation from user (y/N)\n * @param prompt - Confirmation prompt message\n * @returns True if user confirmed (y/yes), false otherwise\n */\nexport async function readConfirmation(prompt: string): Promise<boolean> {\n // If not TTY, auto-confirm\n if (!isTTY()) {\n return true;\n }\n\n // Display prompt\n stdout.write(`${prompt} (y/N): `);\n\n // Read input\n const chunks: Buffer[] = [];\n for await (const chunk of stdin) {\n chunks.push(chunk as Buffer);\n // Break after first line\n const input = Buffer.concat(chunks).toString(\"utf-8\");\n if (input.includes(\"\\n\")) {\n break;\n }\n }\n\n const input = Buffer.concat(chunks).toString(\"utf-8\").trim().toLowerCase();\n return input === \"y\" || input === \"yes\";\n}\n","import type { CslItem } from \"../core/csl-json/types.js\";\n\n/**\n * Client for communicating with the reference-manager HTTP server.\n */\nexport class ServerClient {\n constructor(private baseUrl: string) {}\n\n /**\n * Get all references from the server.\n * @returns Array of CSL items\n */\n async getAll(): Promise<CslItem[]> {\n const url = `${this.baseUrl}/api/references`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(await response.text());\n }\n\n return (await response.json()) as CslItem[];\n }\n\n /**\n * Find reference by UUID.\n * @param uuid - Reference UUID\n * @returns CSL item or null if not found\n */\n async findByUuid(uuid: string): Promise<CslItem | null> {\n const url = `${this.baseUrl}/api/references/${uuid}`;\n const response = await fetch(url);\n\n if (response.status === 404) {\n return null;\n }\n\n if (!response.ok) {\n throw new Error(await response.text());\n }\n\n return (await response.json()) as CslItem;\n }\n\n /**\n * Add new reference to the library.\n * @param item - CSL item to add\n * @returns Created CSL item with UUID\n */\n async add(item: CslItem): Promise<CslItem> {\n const url = `${this.baseUrl}/api/references`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(item),\n });\n\n if (!response.ok) {\n throw new Error(await response.text());\n }\n\n return (await response.json()) as CslItem;\n }\n\n /**\n * Update existing reference.\n * @param uuid - Reference UUID\n * @param item - Updated CSL item\n * @returns Updated CSL item\n */\n async update(uuid: string, item: CslItem): Promise<CslItem> {\n const url = `${this.baseUrl}/api/references/${uuid}`;\n const response = await fetch(url, {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(item),\n });\n\n if (!response.ok) {\n throw new Error(await response.text());\n }\n\n return (await response.json()) as CslItem;\n }\n\n /**\n * Remove reference by UUID.\n * @param uuid - Reference UUID\n */\n async remove(uuid: string): Promise<void> {\n const url = `${this.baseUrl}/api/references/${uuid}`;\n const response = await fetch(url, {\n method: \"DELETE\",\n });\n\n if (!response.ok) {\n throw new Error(await response.text());\n }\n }\n}\n","import { spawn } from \"node:child_process\";\nimport type { Config } from \"../config/schema.js\";\nimport {\n getPortfilePath,\n isProcessRunning,\n portfileExists,\n readPortfile,\n removePortfile,\n} from \"../server/portfile.js\";\n\n/**\n * Server connection information.\n */\nexport interface ServerConnection {\n baseUrl: string;\n pid: number;\n}\n\n/**\n * Get server connection if available.\n * @param libraryPath - Path to the library file\n * @param config - Configuration\n * @returns Server connection or null if not available\n */\nexport async function getServerConnection(\n libraryPath: string,\n config: Config\n): Promise<ServerConnection | null> {\n const portfilePath = getPortfilePath();\n const portfileData = await readPortfile(portfilePath);\n\n // Check if portfile exists\n if (!portfileData) {\n // No server running\n if (config.server.autoStart) {\n // Auto-start server\n await startServerDaemon(libraryPath, config);\n await waitForPortfile(5000); // 5 second timeout\n // Retry connection\n return await getServerConnection(libraryPath, config);\n }\n return null;\n }\n\n // Check if process is running\n if (!isProcessRunning(portfileData.pid)) {\n // Stale portfile, remove it\n await removePortfile(portfilePath);\n return null;\n }\n\n // Check if library path matches\n if (!portfileData.library || portfileData.library !== libraryPath) {\n // Server is serving a different library\n return null;\n }\n\n // Server is running and serving our library\n return {\n baseUrl: `http://localhost:${portfileData.port}`,\n pid: portfileData.pid,\n };\n}\n\n/**\n * Start server in daemon mode.\n * @param libraryPath - Path to the library file\n * @param _config - Configuration (reserved for future use)\n */\nexport async function startServerDaemon(libraryPath: string, _config: Config): Promise<void> {\n // Get the binary path (argv[1] is the script being executed)\n const binaryPath = process.argv[1] || process.execPath;\n\n // Spawn server in detached daemon mode\n const child = spawn(\n process.execPath,\n [binaryPath, \"server\", \"start\", \"--daemon\", \"--library\", libraryPath],\n {\n detached: true,\n stdio: \"ignore\",\n }\n );\n\n child.unref(); // Allow parent to exit\n}\n\n/**\n * Wait for portfile to appear.\n * @param timeoutMs - Timeout in milliseconds\n */\nexport async function waitForPortfile(timeoutMs: number): Promise<void> {\n const portfilePath = getPortfilePath();\n const startTime = Date.now();\n const checkInterval = 50; // Check every 50ms\n\n while (Date.now() - startTime < timeoutMs) {\n if (await portfileExists(portfilePath)) {\n return;\n }\n // Wait before next check\n await new Promise((resolve) => setTimeout(resolve, checkInterval));\n }\n\n throw new Error(`Server failed to start: portfile not created within ${timeoutMs}ms`);\n}\n","/**\n * CLI Entry Point\n */\n\nimport { Command } from \"commander\";\nimport { z } from \"zod\";\nimport type { CslItem } from \"../core/csl-json/types.js\";\nimport { Library } from \"../core/library.js\";\nimport { getPortfilePath } from \"../server/portfile.js\";\nimport { add as addCommand } from \"./commands/add.js\";\nimport { list } from \"./commands/list.js\";\nimport { search as searchCommand } from \"./commands/search.js\";\nimport { serverStart, serverStatus, serverStop } from \"./commands/server.js\";\nimport { update as updateCommand } from \"./commands/update.js\";\nimport type { CliOptions } from \"./helpers.js\";\nimport {\n isTTY,\n loadConfigWithOverrides,\n parseJsonInput,\n readConfirmation,\n readJsonInput,\n} from \"./helpers.js\";\nimport { ServerClient } from \"./server-client.js\";\nimport { getServerConnection } from \"./server-detection.js\";\n\n// Import package.json for version and description\nimport packageJson from \"../../package.json\" with { type: \"json\" };\n\n/**\n * Create Commander program instance\n */\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name(\"reference-manager\")\n .version(packageJson.version)\n .description(packageJson.description);\n\n // Global options\n program\n .option(\"--library <path>\", \"Override library file path\")\n .option(\"--log-level <level>\", \"Override log level (silent|info|debug)\")\n .option(\"--config <path>\", \"Use specific config file\")\n .option(\"--quiet\", \"Suppress all non-error output\")\n .option(\"--verbose\", \"Enable verbose output\")\n .option(\"--no-backup\", \"Disable backup creation\")\n .option(\"--backup-dir <path>\", \"Override backup directory\");\n\n // Register commands\n registerListCommand(program);\n registerSearchCommand(program);\n registerAddCommand(program);\n registerRemoveCommand(program);\n registerUpdateCommand(program);\n registerServerCommand(program);\n\n return program;\n}\n\n/**\n * Register 'list' command\n */\nfunction registerListCommand(program: Command): void {\n program\n .command(\"list\")\n .description(\"List all references in the library\")\n .option(\"--json\", \"Output in JSON format\")\n .option(\"--ids-only\", \"Output only citation keys\")\n .option(\"--uuid\", \"Output only UUIDs\")\n .option(\"--bibtex\", \"Output in BibTeX format\")\n .action(async (options) => {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n\n // Get items (server or direct)\n const server = await getServerConnection(config.library, config);\n let items: CslItem[];\n\n if (server) {\n // Use server API\n const client = new ServerClient(server.baseUrl);\n items = await client.getAll();\n } else {\n // Direct file access\n const library = await Library.load(config.library);\n items = library.getAll().map((ref) => ref.getItem());\n }\n\n // Execute list command\n await list(items, {\n json: options.json,\n idsOnly: options.idsOnly,\n uuid: options.uuid,\n bibtex: options.bibtex,\n });\n\n process.exit(0);\n } catch (error) {\n process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(4);\n }\n });\n}\n\n/**\n * Register 'search' command\n */\nfunction registerSearchCommand(program: Command): void {\n program\n .command(\"search\")\n .description(\"Search references\")\n .argument(\"<query>\", \"Search query\")\n .option(\"--json\", \"Output in JSON format\")\n .option(\"--ids-only\", \"Output only citation keys\")\n .option(\"--uuid\", \"Output only UUIDs\")\n .option(\"--bibtex\", \"Output in BibTeX format\")\n .action(async (query: string, options) => {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n\n // Get items (server or direct)\n const server = await getServerConnection(config.library, config);\n let items: CslItem[];\n\n if (server) {\n // Use server API\n const client = new ServerClient(server.baseUrl);\n items = await client.getAll();\n } else {\n // Direct file access\n const library = await Library.load(config.library);\n items = library.getAll().map((ref) => ref.getItem());\n }\n\n // Execute search command (handles search and output)\n await searchCommand(items, query, {\n json: options.json,\n idsOnly: options.idsOnly,\n uuid: options.uuid,\n bibtex: options.bibtex,\n });\n\n process.exit(0);\n } catch (error) {\n process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(4);\n }\n });\n}\n\n/**\n * Register 'add' command\n */\nasync function addViaServer(\n items: CslItem[],\n server: { baseUrl: string },\n force: boolean\n): Promise<void> {\n const client = new ServerClient(server.baseUrl);\n\n for (const item of items) {\n try {\n await client.add(item);\n process.stderr.write(`Added reference: [${item.id}]\\n`);\n } catch (error) {\n if (!force && error instanceof Error && error.message.includes(\"Duplicate\")) {\n process.stderr.write(`Error: ${error.message}\\n`);\n process.exit(1);\n }\n throw error;\n }\n }\n}\n\nasync function addViaLibrary(items: CslItem[], libraryPath: string, force: boolean): Promise<void> {\n const library = await Library.load(libraryPath);\n const existingItems = library.getAll().map((ref) => ref.getItem());\n\n for (const item of items) {\n const result = await addCommand(existingItems, item, { force });\n\n if (result.added) {\n library.add(result.item);\n process.stderr.write(`Added reference: [${result.item.id}]\\n`);\n existingItems.push(result.item);\n } else if (result.duplicate) {\n throw new Error(\n `Duplicate detected: ${result.duplicate.type} match with existing reference [${result.duplicate.existing.id}]`\n );\n }\n }\n\n await library.save();\n}\n\nfunction handleAddError(error: unknown): never {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"Parse error\")) {\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(3);\n }\n if (message.includes(\"Duplicate\")) {\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(1);\n }\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(4);\n}\n\nasync function handleAddAction(\n file: string | undefined,\n options: CliOptions & { force?: boolean },\n program: Command\n): Promise<void> {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n\n const inputStr = await readJsonInput(file);\n const input = parseJsonInput(inputStr);\n const items: CslItem[] = Array.isArray(input) ? input : [input as CslItem];\n\n const server = await getServerConnection(config.library, config);\n\n if (server) {\n await addViaServer(items, server, options.force ?? false);\n } else {\n await addViaLibrary(items, config.library, options.force ?? false);\n }\n\n process.exit(0);\n } catch (error) {\n handleAddError(error);\n }\n}\n\nfunction registerAddCommand(program: Command): void {\n program\n .command(\"add\")\n .description(\"Add new reference(s) to the library\")\n .argument(\"[file]\", \"JSON file to add (or use stdin)\")\n .option(\"-f, --force\", \"Skip duplicate detection\")\n .action(async (file: string | undefined, options) => {\n await handleAddAction(file, options, program);\n });\n}\n\n/**\n * Register 'remove' command\n */\nasync function findReferenceToRemove(\n identifier: string,\n byUuid: boolean,\n server: { baseUrl: string } | null,\n libraryPath: string\n): Promise<CslItem | undefined> {\n if (server) {\n const client = new ServerClient(server.baseUrl);\n const items = await client.getAll();\n return byUuid\n ? items.find((item) => item.custom?.uuid === identifier)\n : items.find((item) => item.id === identifier);\n }\n\n const library = await Library.load(libraryPath);\n const ref = byUuid ? library.findByUuid(identifier) : library.findById(identifier);\n return ref?.getItem();\n}\n\nasync function confirmRemoval(refToRemove: CslItem, force: boolean): Promise<boolean> {\n if (force || !isTTY()) {\n return true;\n }\n\n const authors = Array.isArray(refToRemove.author)\n ? refToRemove.author.map((a) => `${a.family || \"\"}, ${a.given?.[0] || \"\"}.`).join(\"; \")\n : \"(no authors)\";\n const confirmMsg = `Remove reference [${refToRemove.id}]?\\nTitle: ${refToRemove.title || \"(no title)\"}\\nAuthors: ${authors}\\nContinue?`;\n\n return await readConfirmation(confirmMsg);\n}\n\nasync function removeReference(\n identifier: string,\n refToRemove: CslItem,\n byUuid: boolean,\n server: { baseUrl: string } | null,\n libraryPath: string\n): Promise<void> {\n if (server) {\n const client = new ServerClient(server.baseUrl);\n if (!refToRemove.custom?.uuid) {\n throw new Error(\"Reference missing UUID\");\n }\n await client.remove(refToRemove.custom.uuid);\n return;\n }\n\n const library = await Library.load(libraryPath);\n const removed = byUuid ? library.removeByUuid(identifier) : library.removeById(identifier);\n\n if (!removed) {\n throw new Error(\"Reference not found\");\n }\n\n await library.save();\n}\n\nfunction handleRemoveError(error: unknown): never {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"not found\")) {\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(1);\n }\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(4);\n}\n\nasync function handleRemoveAction(\n identifier: string,\n options: { uuid?: boolean; force?: boolean },\n program: Command\n): Promise<void> {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n const server = await getServerConnection(config.library, config);\n\n const refToRemove = await findReferenceToRemove(\n identifier,\n options.uuid ?? false,\n server,\n config.library\n );\n\n if (!refToRemove) {\n process.stderr.write(`Error: Reference not found: ${identifier}\\n`);\n process.exit(1);\n }\n\n const confirmed = await confirmRemoval(refToRemove, options.force ?? false);\n if (!confirmed) {\n process.stderr.write(\"Cancelled.\\n\");\n process.exit(2);\n }\n\n await removeReference(identifier, refToRemove, options.uuid ?? false, server, config.library);\n\n process.stderr.write(`Removed reference: [${refToRemove.id}]\\n`);\n process.exit(0);\n } catch (error) {\n handleRemoveError(error);\n }\n}\n\nfunction registerRemoveCommand(program: Command): void {\n program\n .command(\"remove\")\n .description(\"Remove a reference from the library\")\n .argument(\"<identifier>\", \"Citation key or UUID\")\n .option(\"--uuid\", \"Interpret identifier as UUID\")\n .option(\"-f, --force\", \"Skip confirmation prompt\")\n .action(async (identifier: string, options) => {\n await handleRemoveAction(identifier, options, program);\n });\n}\n\n/**\n * Register 'update' command\n */\nasync function updateViaServer(\n identifier: string,\n updates: Record<string, unknown>,\n byUuid: boolean,\n server: { baseUrl: string }\n): Promise<void> {\n const client = new ServerClient(server.baseUrl);\n const items = await client.getAll();\n const refToUpdate = byUuid\n ? items.find((item) => item.custom?.uuid === identifier)\n : items.find((item) => item.id === identifier);\n\n if (!refToUpdate || !refToUpdate.custom?.uuid) {\n process.stderr.write(`Error: Reference not found: ${identifier}\\n`);\n process.exit(1);\n }\n\n const updatedItem = { ...refToUpdate, ...updates } as CslItem;\n await client.update(refToUpdate.custom.uuid, updatedItem);\n}\n\nasync function updateViaLibrary(\n identifier: string,\n updates: Partial<CslItem>,\n byUuid: boolean,\n libraryPath: string\n): Promise<void> {\n const library = await Library.load(libraryPath);\n const items = library.getAll().map((ref) => ref.getItem());\n\n const result = await updateCommand(items, identifier, updates, { byUuid });\n\n if (!result.updated || !result.item) {\n throw new Error(\"Reference not found\");\n }\n\n if (result.item.custom?.uuid) {\n library.removeByUuid(result.item.custom.uuid);\n library.add(result.item);\n }\n\n await library.save();\n}\n\nfunction handleUpdateError(error: unknown): never {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"Parse error\")) {\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(3);\n }\n if (message.includes(\"not found\") || message.includes(\"validation\")) {\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(1);\n }\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(4);\n}\n\nasync function handleUpdateAction(\n identifier: string,\n file: string | undefined,\n options: { uuid?: boolean },\n program: Command\n): Promise<void> {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n\n const inputStr = await readJsonInput(file);\n const updates = parseJsonInput(inputStr);\n\n // Validate that updates is a non-null object using zod\n const updatesSchema = z.record(z.string(), z.unknown());\n const validatedUpdates = updatesSchema.parse(updates);\n\n const server = await getServerConnection(config.library, config);\n\n if (server) {\n await updateViaServer(identifier, validatedUpdates, options.uuid ?? false, server);\n } else {\n await updateViaLibrary(\n identifier,\n validatedUpdates as Partial<CslItem>,\n options.uuid ?? false,\n config.library\n );\n }\n\n process.stderr.write(`Updated reference: [${identifier}]\\n`);\n process.exit(0);\n } catch (error) {\n handleUpdateError(error);\n }\n}\n\nfunction registerUpdateCommand(program: Command): void {\n program\n .command(\"update\")\n .description(\"Update fields of an existing reference\")\n .argument(\"<identifier>\", \"Citation key or UUID\")\n .argument(\"[file]\", \"JSON file with updates (or use stdin)\")\n .option(\"--uuid\", \"Interpret identifier as UUID\")\n .action(async (identifier: string, file: string | undefined, options) => {\n await handleUpdateAction(identifier, file, options, program);\n });\n}\n\n/**\n * Register 'server' command\n */\nfunction registerServerCommand(program: Command): void {\n const serverCmd = program.command(\"server\").description(\"Manage HTTP server for library access\");\n\n serverCmd\n .command(\"start\")\n .description(\"Start HTTP server\")\n .option(\"--port <port>\", \"Specify port number\")\n .option(\"-d, --daemon\", \"Run in background\")\n .action(async (options) => {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n\n const portfilePath = getPortfilePath();\n\n const startOptions = {\n library: config.library,\n portfilePath,\n daemon: options.daemon,\n ...(options.port && { port: Number.parseInt(options.port, 10) }),\n };\n\n await serverStart(startOptions);\n\n process.exit(0);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"already running\") || message.includes(\"conflict\")) {\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(1);\n }\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(4);\n }\n });\n\n serverCmd\n .command(\"stop\")\n .description(\"Stop running server\")\n .action(async () => {\n try {\n const portfilePath = getPortfilePath();\n await serverStop(portfilePath);\n\n process.stderr.write(\"Server stopped.\\n\");\n process.exit(0);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"not running\")) {\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(1);\n }\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(4);\n }\n });\n\n serverCmd\n .command(\"status\")\n .description(\"Check server status\")\n .action(async () => {\n try {\n const portfilePath = getPortfilePath();\n const status = await serverStatus(portfilePath);\n\n if (status) {\n process.stdout.write(\n `Server is running\\nPort: ${status.port}\\nPID: ${status.pid}\\nLibrary: ${status.library}\\n`\n );\n process.exit(0);\n } else {\n process.stdout.write(\"Server not running\\n\");\n process.exit(1);\n }\n } catch (error) {\n process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(4);\n }\n });\n}\n\n/**\n * Main CLI entry point\n */\nexport async function main(argv: string[]): Promise<void> {\n const program = createProgram();\n\n // Setup signal handlers\n process.on(\"SIGINT\", () => {\n process.exit(130);\n });\n\n process.on(\"SIGTERM\", () => {\n process.exit(0);\n });\n\n await program.parseAsync(argv);\n}\n"],"names":["fs","searchReferences","input","searchCommand","addCommand","updateCommand"],"mappings":";;;;;;;;AAQO,SAAS,kBAA0B;AACxC,QAAM,SAAS,GAAG,OAAA;AAClB,SAAO,KAAK,KAAK,QAAQ,qBAAqB,aAAa;AAC7D;AAUA,eAAsB,cACpB,cACA,MACA,KACA,SACA,YACe;AAEf,QAAM,MAAM,KAAK,QAAQ,YAAY;AACrC,QAAMA,SAAG,MAAM,KAAK,EAAE,WAAW,MAAM;AAGvC,QAAM,OAAgC,EAAE,MAAM,KAAK,QAAA;AACnD,MAAI,eAAe,QAAW;AAC5B,SAAK,aAAa;AAAA,EACpB;AACA,QAAM,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC;AAC5C,QAAMA,SAAG,UAAU,cAAc,SAAS,OAAO;AACnD;AAOA,eAAsB,aAAa,cAKzB;AACR,MAAI;AACF,UAAM,UAAU,MAAMA,SAAG,SAAS,cAAc,OAAO;AACvD,UAAM,OAAO,KAAK,MAAM,OAAO;AAG/B,QAAI,OAAO,KAAK,SAAS,YAAY,OAAO,KAAK,QAAQ,UAAU;AACjE,aAAO;AAAA,IACT;AAGA,UAAM,SAKF;AAAA,MACF,MAAM,KAAK;AAAA,MACX,KAAK,KAAK;AAAA,IAAA;AAIZ,QAAI,OAAO,KAAK,YAAY,UAAU;AACpC,aAAO,UAAU,KAAK;AAAA,IACxB;AACA,QAAI,OAAO,KAAK,eAAe,UAAU;AACvC,aAAO,aAAa,KAAK;AAAA,IAC3B;AAEA,WAAO;AAAA,EACT,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;AAOA,eAAsB,eAAe,cAAwC;AAC3E,MAAI;AACF,UAAMA,SAAG,OAAO,YAAY;AAC5B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,eAAe,cAAqC;AACxE,MAAI;AACF,UAAMA,SAAG,OAAO,YAAY;AAAA,EAC9B,QAAQ;AAAA,EAER;AACF;AAOO,SAAS,iBAAiB,KAAsB;AACrD,MAAI,OAAO,GAAG;AACZ,WAAO;AAAA,EACT;AAEA,MAAI;AAEF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AC/GA,SAAS,eAAe,OAAuB;AAC7C,QAAM,WAAW;AACjB,MAAI,SAAS;AACb,MAAI,IAAI;AAER,KAAG;AACD,aAAS,SAAS,IAAI,EAAE,IAAI;AAC5B,QAAI,KAAK,MAAM,IAAI,EAAE,IAAI;AAAA,EAC3B,SAAS,KAAK;AAEd,SAAO;AACT;AAKA,SAAS,mBAAmB,QAAgB,UAAuD;AACjG,QAAM,cAAc,IAAI,IAAI,SAAS,IAAI,CAAC,SAAS,KAAK,EAAE,CAAC;AAE3D,MAAI,CAAC,YAAY,IAAI,MAAM,GAAG;AAC5B,WAAO,EAAE,IAAI,QAAQ,SAAS,MAAA;AAAA,EAChC;AAGA,MAAI,QAAQ;AACZ,MAAI;AAEJ,KAAG;AACD,UAAM,SAAS,eAAe,KAAK;AACnC,YAAQ,GAAG,MAAM,GAAG,MAAM;AAC1B;AAAA,EACF,SAAS,YAAY,IAAI,KAAK;AAE9B,SAAO,EAAE,IAAI,OAAO,SAAS,KAAA;AAC/B;AAUA,eAAsB,IACpB,UACA,SACA,SACoB;AAEpB,MAAI,CAAC,QAAQ,OAAO;AAClB,UAAM,kBAAkB,gBAAgB,SAAS,QAAQ;AAEzD,QAAI,gBAAgB,QAAQ,SAAS,GAAG;AAEtC,aAAO;AAAA,QACL,OAAO;AAAA,QACP,MAAM;AAAA,QACN,WAAW;AAAA,QACX,WAAW,gBAAgB,QAAQ,CAAC;AAAA,MAAA;AAAA,IAExC;AAAA,EACF;AAGA,QAAM,aAAa,QAAQ;AAC3B,QAAM,EAAE,IAAI,QAAA,IAAY,mBAAmB,YAAY,QAAQ;AAE/D,QAAM,YAAqB;AAAA,IACzB,GAAG;AAAA,IACH;AAAA,EAAA;AAGF,SAAO;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,IACX,YAAY,UAAU,aAAa;AAAA,EAAA;AAEvC;AC5FA,SAAS,aAAa,SAAyB;AAC7C,QAAM,UAAkC;AAAA,IACtC,SAAS;AAAA,IACT,mBAAmB;AAAA,IACnB,oBAAoB;AAAA,IACpB,qBAAqB;AAAA,IACrB,MAAM;AAAA,IACN,SAAS;AAAA,IACT,oBAAoB;AAAA,IACpB,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,SAAS;AAAA,EAAA;AAGX,SAAO,QAAQ,OAAO,KAAK;AAC7B;AAKA,SAAS,mBAAmB,QAIjB;AACT,MAAI,OAAO,SAAS;AAClB,WAAO,OAAO;AAAA,EAChB;AACA,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,QAAQ,OAAO,SAAS;AAC9B,SAAO,QAAQ,GAAG,MAAM,KAAK,KAAK,KAAK;AACzC;AAKA,SAAS,oBACP,SAQQ;AACR,SAAO,QAAQ,IAAI,kBAAkB,EAAE,KAAK,OAAO;AACrD;AAKA,SAAS,YAAY,MAAc,OAAuB;AACxD,SAAO,KAAK,IAAI,OAAO,KAAK;AAC9B;AAKA,SAAS,eAAe,OAAiB,MAAqB;AAE5D,MAAI,KAAK,OAAO;AACd,UAAM,KAAK,YAAY,SAAS,KAAK,KAAK,CAAC;AAAA,EAC7C;AAGA,MAAI,KAAK,UAAU,KAAK,OAAO,SAAS,GAAG;AACzC,UAAM,KAAK,YAAY,UAAU,oBAAoB,KAAK,MAAM,CAAC,CAAC;AAAA,EACpE;AAGA,QAAM,OAAO,KAAK,SAAS,YAAY,IAAI,CAAC,IAAI,CAAC;AACjD,MAAI,MAAM;AACR,UAAM,KAAK,YAAY,QAAQ,OAAO,IAAI,CAAC,CAAC;AAAA,EAC9C;AACF;AAKA,SAAS,sBAAsB,OAAiB,MAAe,WAAyB;AAEtF,MAAI,KAAK,iBAAiB,GAAG;AAC3B,QAAI,cAAc,WAAW;AAC3B,YAAM,KAAK,YAAY,WAAW,KAAK,iBAAiB,CAAC,CAAC;AAAA,IAC5D,WAAW,cAAc,YAAY,cAAc,iBAAiB;AAClE,YAAM,KAAK,YAAY,aAAa,KAAK,iBAAiB,CAAC,CAAC;AAAA,IAC9D;AAAA,EACF;AAGA,MAAI,KAAK,QAAQ;AACf,UAAM,KAAK,YAAY,UAAU,KAAK,MAAM,CAAC;AAAA,EAC/C;AAGA,MAAI,KAAK,OAAO;AACd,UAAM,KAAK,YAAY,UAAU,KAAK,KAAK,CAAC;AAAA,EAC9C;AAGA,MAAI,KAAK,MAAM;AACb,UAAM,KAAK,YAAY,SAAS,KAAK,IAAI,CAAC;AAAA,EAC5C;AAGA,MAAI,KAAK,WAAW;AAClB,UAAM,KAAK,YAAY,aAAa,KAAK,SAAS,CAAC;AAAA,EACrD;AACF;AAKA,SAAS,oBAAoB,OAAiB,MAAqB;AAEjE,MAAI,KAAK,KAAK;AACZ,UAAM,KAAK,YAAY,OAAO,KAAK,GAAG,CAAC;AAAA,EACzC;AAGA,MAAI,KAAK,KAAK;AACZ,UAAM,KAAK,YAAY,OAAO,KAAK,GAAG,CAAC;AAAA,EACzC;AAGA,MAAI,KAAK,MAAM;AACb,UAAM,KAAK,YAAY,QAAQ,SAAS,KAAK,IAAI,EAAE,CAAC;AAAA,EACtD,WAAW,KAAK,OAAO;AACrB,UAAM,KAAK,YAAY,QAAQ,UAAU,KAAK,KAAK,EAAE,CAAC;AAAA,EACxD;AACF;AAKA,SAAS,wBAAwB,KAAwB;AACvD,QAAM,OAAgB,IAAI,QAAA;AAC1B,QAAM,YAAY,aAAa,KAAK,IAAI;AACxC,QAAM,QAAkB,CAAA;AAGxB,QAAM,KAAK,IAAI,SAAS,IAAI,KAAK,EAAE,GAAG;AAGtC,iBAAe,OAAO,IAAI;AAC1B,wBAAsB,OAAO,MAAM,SAAS;AAC5C,sBAAoB,OAAO,IAAI;AAG/B,QAAM,KAAK,GAAG;AAEd,SAAO,MAAM,KAAK,IAAI;AACxB;AAKO,SAAS,aAAa,YAAiC;AAC5D,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO;AAAA,EACT;AAEA,SAAO,WAAW,IAAI,uBAAuB,EAAE,KAAK,MAAM;AAC5D;ACrKO,SAAS,WAAW,YAAiC;AAC1D,QAAM,QAAQ,WAAW,IAAI,CAAC,QAAQ,IAAI,SAAS;AACnD,SAAO,KAAK,UAAU,KAAK;AAC7B;ACHA,SAAS,aAAa,QAGX;AACT,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,eAAe,OAAO,QAAQ,GAAG,OAAO,MAAM,OAAO,CAAC,CAAC,MAAM;AACnE,SAAO,eAAe,GAAG,MAAM,KAAK,YAAY,KAAK;AACvD;AAKA,SAAS,cACP,SAQQ;AACR,SAAO,QAAQ,IAAI,YAAY,EAAE,KAAK,IAAI;AAC5C;AAKA,SAAS,sBAAsB,KAAwB;AACrD,QAAM,OAAO,IAAI,QAAA;AACjB,QAAM,QAAkB,CAAA;AAGxB,QAAM,SAAS,KAAK,QAAQ,IAAI,KAAK,EAAE,KAAK,KAAK,KAAK,KAAK,IAAI,KAAK,EAAE;AACtE,QAAM,KAAK,MAAM;AAGjB,MAAI,KAAK,UAAU,KAAK,OAAO,SAAS,GAAG;AACzC,UAAM,KAAK,cAAc,cAAc,KAAK,MAAM,CAAC,EAAE;AAAA,EACvD;AAGA,QAAM,OAAO,KAAK,SAAS,YAAY,IAAI,CAAC,IAAI,CAAC;AACjD,QAAM,KAAK,WAAW,QAAQ,WAAW,EAAE;AAG3C,QAAM,KAAK,WAAW,KAAK,IAAI,EAAE;AAGjC,MAAI,KAAK,KAAK;AACZ,UAAM,KAAK,UAAU,KAAK,GAAG,EAAE;AAAA,EACjC;AAGA,MAAI,KAAK,MAAM;AACb,UAAM,KAAK,WAAW,KAAK,IAAI,EAAE;AAAA,EACnC;AAGA,MAAI,KAAK,OAAO;AACd,UAAM,KAAK,YAAY,KAAK,KAAK,EAAE;AAAA,EACrC;AAGA,MAAI,KAAK,KAAK;AACZ,UAAM,KAAK,UAAU,KAAK,GAAG,EAAE;AAAA,EACjC;AAGA,QAAM,KAAK,WAAW,IAAI,QAAA,CAAS,EAAE;AAErC,SAAO,MAAM,KAAK,IAAI;AACxB;AAKO,SAAS,aAAa,YAAiC;AAC5D,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO;AAAA,EACT;AAEA,SAAO,WAAW,IAAI,qBAAqB,EAAE,KAAK,MAAM;AAC1D;ACrEA,eAAsB,KAAK,OAAkB,SAAqC;AAEhF,QAAM,gBAAgB,CAAC,QAAQ,MAAM,QAAQ,SAAS,QAAQ,MAAM,QAAQ,MAAM,EAAE;AAAA,IAClF;AAAA,EAAA;AAGF,MAAI,cAAc,SAAS,GAAG;AAC5B,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAGA,QAAM,aAAa,MAAM,IAAI,CAAC,SAAS,IAAI,UAAU,IAAI,CAAC;AAG1D,MAAI,QAAQ,MAAM;AAChB,YAAQ,OAAO,MAAM,WAAW,UAAU,CAAC;AAAA,EAC7C,WAAW,QAAQ,SAAS;AAC1B,eAAW,QAAQ,OAAO;AACxB,cAAQ,OAAO,MAAM,GAAG,KAAK,EAAE;AAAA,CAAI;AAAA,IACrC;AAAA,EACF,WAAW,QAAQ,MAAM;AACvB,eAAW,QAAQ,OAAO;AACxB,UAAI,KAAK,QAAQ;AACf,gBAAQ,OAAO,MAAM,GAAG,KAAK,OAAO,IAAI;AAAA,CAAI;AAAA,MAC9C;AAAA,IACF;AAAA,EACF,WAAW,QAAQ,QAAQ;AACzB,YAAQ,OAAO,MAAM,aAAa,UAAU,CAAC;AAAA,EAC/C,OAAO;AAEL,YAAQ,OAAO,MAAM,aAAa,UAAU,CAAC;AAAA,EAC/C;AACF;AC9BA,eAAsB,OACpB,OACA,OACA,SACe;AAEf,QAAM,gBAAgB,CAAC,QAAQ,MAAM,QAAQ,SAAS,QAAQ,MAAM,QAAQ,MAAM,EAAE;AAAA,IAClF;AAAA,EAAA;AAGF,MAAI,cAAc,SAAS,GAAG;AAC5B,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAGA,QAAM,cAAc,SAAS,KAAK;AAGlC,QAAM,UAAUC,SAAiB,OAAO,YAAY,MAAM;AAG1D,QAAM,SAAS,YAAY,OAAO;AAGlC,QAAM,eAAe,OAAO,IAAI,CAAC,WAAW,OAAO,SAAS;AAG5D,QAAM,aAAa,aAAa,IAAI,CAAC,SAAS,IAAI,UAAU,IAAI,CAAC;AAGjE,MAAI,QAAQ,MAAM;AAChB,YAAQ,OAAO,MAAM,WAAW,UAAU,CAAC;AAAA,EAC7C,WAAW,QAAQ,SAAS;AAC1B,eAAW,QAAQ,cAAc;AAC/B,cAAQ,OAAO,MAAM,GAAG,KAAK,EAAE;AAAA,CAAI;AAAA,IACrC;AAAA,EACF,WAAW,QAAQ,MAAM;AACvB,eAAW,QAAQ,cAAc;AAC/B,UAAI,KAAK,QAAQ;AACf,gBAAQ,OAAO,MAAM,GAAG,KAAK,OAAO,IAAI;AAAA,CAAI;AAAA,MAC9C;AAAA,IACF;AAAA,EACF,WAAW,QAAQ,QAAQ;AACzB,YAAQ,OAAO,MAAM,aAAa,UAAU,CAAC;AAAA,EAC/C,OAAO;AAEL,YAAQ,OAAO,MAAM,aAAa,UAAU,CAAC;AAAA,EAC/C;AACF;AC/CA,eAAsB,YAAY,SAA4C;AAE5E,QAAM,iBAAiB,MAAM,aAAa,QAAQ,YAAY;AAC9D,MAAI,mBAAmB,MAAM;AAC3B,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AAGA,QAAM,OAAO,QAAQ,QAAQ;AAI7B,QAAM,MAAM,QAAQ;AACpB,QAAM,cAAa,oBAAI,KAAA,GAAO,YAAA;AAE9B,QAAM,cAAc,QAAQ,cAAc,MAAM,KAAK,QAAQ,SAAS,UAAU;AAIlF;AAOA,eAAsB,WAAW,cAAqC;AAEpE,QAAM,SAAS,MAAM,aAAa,YAAY;AAC9C,MAAI,WAAW,MAAM;AACnB,UAAM,IAAI,MAAM,uBAAuB;AAAA,EACzC;AAIA,QAAM,eAAe,YAAY;AAEjC,UAAQ,OAAO,MAAM,+BAA+B;AACtD;AAQA,eAAsB,aAAa,cAAkD;AAEnF,QAAM,eAAe,MAAM,aAAa,YAAY;AACpD,MAAI,iBAAiB,MAAM;AACzB,WAAO;AAAA,EACT;AAGA,MAAI,CAAC,iBAAiB,aAAa,GAAG,GAAG;AAEvC,UAAM,eAAe,YAAY;AACjC,WAAO;AAAA,EACT;AAGA,QAAM,SAAqB;AAAA,IACzB,MAAM,aAAa;AAAA,IACnB,KAAK,aAAa;AAAA,IAClB,SAAS,aAAa,WAAW;AAAA,EAAA;AAGnC,MAAI,aAAa,eAAe,QAAW;AACzC,WAAO,aAAa,aAAa;AAAA,EACnC;AAEA,SAAO;AACT;AC7EA,eAAsB,OACpB,OACA,YACA,SACA,SACuB;AACvB,MAAI,aAAa;AAEjB,MAAI,QAAQ,QAAQ;AAElB,iBAAa,MAAM,UAAU,CAAC,SAAS,KAAK,QAAQ,SAAS,UAAU;AAAA,EACzE,OAAO;AAEL,iBAAa,MAAM,UAAU,CAAC,SAAS,KAAK,OAAO,UAAU;AAAA,EAC/D;AAEA,MAAI,eAAe,IAAI;AAErB,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,IAAA;AAAA,EAEJ;AAEA,QAAM,eAAe,MAAM,UAAU;AACrC,MAAI,CAAC,cAAc;AAEjB,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,IAAA;AAAA,EAEJ;AAGA,QAAM,cAAuB;AAAA,IAC3B,GAAG;AAAA,IACH,GAAG;AAAA;AAAA,IAEH,IAAI,QAAQ,MAAM,aAAa;AAAA,IAC/B,MAAM,QAAQ,QAAQ,aAAa;AAAA;AAAA,IAEnC,QAAQ;AAAA,MACN,GAAI,aAAa,UAAU,CAAA;AAAA,MAC3B,GAAI,QAAQ,UAAU,CAAA;AAAA,MACtB,MAAM,aAAa,QAAQ,QAAQ;AAAA,MACnC,YAAY,aAAa,QAAQ,eAAc,oBAAI,KAAA,GAAO,YAAA;AAAA;AAAA,MAE1D,YAAW,oBAAI,KAAA,GAAO,YAAA;AAAA,IAAY;AAAA,EACpC;AAGF,QAAM,eAAe,CAAC,GAAG,MAAM,MAAM,GAAG,UAAU,GAAG,aAAa,GAAG,MAAM,MAAM,aAAa,CAAC,CAAC;AAEhG,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,EAAA;AAEX;AC3CA,eAAsB,cAAc,MAAgC;AAClE,MAAI,MAAM;AAER,QAAI;AACF,aAAO,aAAa,MAAM,OAAO;AAAA,IACnC,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,+BAA+B,IAAI,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MAAA;AAAA,IAElG;AAAA,EACF;AAGA,QAAM,SAAmB,CAAA;AACzB,mBAAiB,SAAS,OAAO;AAC/B,WAAO,KAAK,KAAe;AAAA,EAC7B;AACA,SAAO,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO;AAC/C;AAOO,SAAS,eAAe,OAAwB;AACrD,MAAI,CAAC,SAAS,MAAM,KAAA,MAAW,IAAI;AACjC,UAAM,IAAI,MAAM,0BAA0B;AAAA,EAC5C;AAEA,MAAI;AACF,WAAO,KAAK,MAAM,KAAK;AAAA,EACzB,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,8BAA8B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,IAAA;AAAA,EAExF;AACF;AAOA,eAAsB,wBAAwB,SAAsC;AAGlF,QAAM,SAAS,MAAM,WAAA;AAGrB,QAAM,YAA6B,CAAA;AAGnC,MAAI,QAAQ,SAAS;AACnB,cAAU,UAAU,QAAQ;AAAA,EAC9B;AAGA,MAAI,QAAQ,OAAO;AACjB,cAAU,WAAW;AAAA,EACvB,WAAW,QAAQ,SAAS;AAC1B,cAAU,WAAW;AAAA,EACvB,WAAW,QAAQ,UAAU;AAC3B,cAAU,WAAW,QAAQ;AAAA,EAC/B;AAGA,MAAI,QAAQ,WAAW,UAAa,QAAQ,WAAW;AACrD,cAAU,SAAS;AAAA,MACjB,GAAG,OAAO;AAAA,MACV,GAAI,QAAQ,WAAW,UAAa,EAAE,SAAS,QAAQ,OAAA;AAAA,MACvD,GAAI,QAAQ,aAAa,EAAE,WAAW,QAAQ,UAAA;AAAA,IAAU;AAAA,EAE5D;AAEA,SAAO,EAAE,GAAG,QAAQ,GAAG,UAAA;AACzB;AA6BO,SAAS,QAAiB;AAC/B,SAAO,QAAQ,MAAM,SAAS,OAAO,KAAK;AAC5C;AAOA,eAAsB,iBAAiB,QAAkC;AAEvE,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAGA,SAAO,MAAM,GAAG,MAAM,UAAU;AAGhC,QAAM,SAAmB,CAAA;AACzB,mBAAiB,SAAS,OAAO;AAC/B,WAAO,KAAK,KAAe;AAE3B,UAAMC,SAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO;AACpD,QAAIA,OAAM,SAAS,IAAI,GAAG;AACxB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO,EAAE,KAAA,EAAO,YAAA;AAC7D,SAAO,UAAU,OAAO,UAAU;AACpC;ACvKO,MAAM,aAAa;AAAA,EACxB,YAAoB,SAAiB;AAAjB,SAAA,UAAA;AAAA,EAAkB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtC,MAAM,SAA6B;AACjC,UAAM,MAAM,GAAG,KAAK,OAAO;AAC3B,UAAM,WAAW,MAAM,MAAM,GAAG;AAEhC,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,MAAM,SAAS,MAAM;AAAA,IACvC;AAEA,WAAQ,MAAM,SAAS,KAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAW,MAAuC;AACtD,UAAM,MAAM,GAAG,KAAK,OAAO,mBAAmB,IAAI;AAClD,UAAM,WAAW,MAAM,MAAM,GAAG;AAEhC,QAAI,SAAS,WAAW,KAAK;AAC3B,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,MAAM,SAAS,MAAM;AAAA,IACvC;AAEA,WAAQ,MAAM,SAAS,KAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IAAI,MAAiC;AACzC,UAAM,MAAM,GAAG,KAAK,OAAO;AAC3B,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAA;AAAA,MAC3B,MAAM,KAAK,UAAU,IAAI;AAAA,IAAA,CAC1B;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,MAAM,SAAS,MAAM;AAAA,IACvC;AAEA,WAAQ,MAAM,SAAS,KAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,MAAc,MAAiC;AAC1D,UAAM,MAAM,GAAG,KAAK,OAAO,mBAAmB,IAAI;AAClD,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAA;AAAA,MAC3B,MAAM,KAAK,UAAU,IAAI;AAAA,IAAA,CAC1B;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,MAAM,SAAS,MAAM;AAAA,IACvC;AAEA,WAAQ,MAAM,SAAS,KAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,MAA6B;AACxC,UAAM,MAAM,GAAG,KAAK,OAAO,mBAAmB,IAAI;AAClD,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,IAAA,CACT;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,MAAM,SAAS,MAAM;AAAA,IACvC;AAAA,EACF;AACF;AC1EA,eAAsB,oBACpB,aACA,QACkC;AAClC,QAAM,eAAe,gBAAA;AACrB,QAAM,eAAe,MAAM,aAAa,YAAY;AAGpD,MAAI,CAAC,cAAc;AAEjB,QAAI,OAAO,OAAO,WAAW;AAE3B,YAAM,kBAAkB,WAAmB;AAC3C,YAAM,gBAAgB,GAAI;AAE1B,aAAO,MAAM,oBAAoB,aAAa,MAAM;AAAA,IACtD;AACA,WAAO;AAAA,EACT;AAGA,MAAI,CAAC,iBAAiB,aAAa,GAAG,GAAG;AAEvC,UAAM,eAAe,YAAY;AACjC,WAAO;AAAA,EACT;AAGA,MAAI,CAAC,aAAa,WAAW,aAAa,YAAY,aAAa;AAEjE,WAAO;AAAA,EACT;AAGA,SAAO;AAAA,IACL,SAAS,oBAAoB,aAAa,IAAI;AAAA,IAC9C,KAAK,aAAa;AAAA,EAAA;AAEtB;AAOA,eAAsB,kBAAkB,aAAqB,SAAgC;AAE3F,QAAM,aAAa,QAAQ,KAAK,CAAC,KAAK,QAAQ;AAG9C,QAAM,QAAQ;AAAA,IACZ,QAAQ;AAAA,IACR,CAAC,YAAY,UAAU,SAAS,YAAY,aAAa,WAAW;AAAA,IACpE;AAAA,MACE,UAAU;AAAA,MACV,OAAO;AAAA,IAAA;AAAA,EACT;AAGF,QAAM,MAAA;AACR;AAMA,eAAsB,gBAAgB,WAAkC;AACtE,QAAM,eAAe,gBAAA;AACrB,QAAM,YAAY,KAAK,IAAA;AACvB,QAAM,gBAAgB;AAEtB,SAAO,KAAK,QAAQ,YAAY,WAAW;AACzC,QAAI,MAAM,eAAe,YAAY,GAAG;AACtC;AAAA,IACF;AAEA,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,aAAa,CAAC;AAAA,EACnE;AAEA,QAAM,IAAI,MAAM,uDAAuD,SAAS,IAAI;AACtF;;;;;;;ACzEO,SAAS,gBAAyB;AACvC,QAAM,UAAU,IAAI,QAAA;AAEpB,UACG,KAAK,mBAAmB,EACxB,QAAQ,YAAY,OAAO,EAC3B,YAAY,YAAY,WAAW;AAGtC,UACG,OAAO,oBAAoB,4BAA4B,EACvD,OAAO,uBAAuB,wCAAwC,EACtE,OAAO,mBAAmB,0BAA0B,EACpD,OAAO,WAAW,+BAA+B,EACjD,OAAO,aAAa,uBAAuB,EAC3C,OAAO,eAAe,yBAAyB,EAC/C,OAAO,uBAAuB,2BAA2B;AAG5D,sBAAoB,OAAO;AAC3B,wBAAsB,OAAO;AAC7B,qBAAmB,OAAO;AAC1B,wBAAsB,OAAO;AAC7B,wBAAsB,OAAO;AAC7B,wBAAsB,OAAO;AAE7B,SAAO;AACT;AAKA,SAAS,oBAAoB,SAAwB;AACnD,UACG,QAAQ,MAAM,EACd,YAAY,oCAAoC,EAChD,OAAO,UAAU,uBAAuB,EACxC,OAAO,cAAc,2BAA2B,EAChD,OAAO,UAAU,mBAAmB,EACpC,OAAO,YAAY,yBAAyB,EAC5C,OAAO,OAAO,YAAY;AACzB,QAAI;AACF,YAAM,aAAa,QAAQ,KAAA;AAC3B,YAAM,SAAS,MAAM,wBAAwB,EAAE,GAAG,YAAY,GAAG,SAAS;AAG1E,YAAM,SAAS,MAAM,oBAAoB,OAAO,SAAS,MAAM;AAC/D,UAAI;AAEJ,UAAI,QAAQ;AAEV,cAAM,SAAS,IAAI,aAAa,OAAO,OAAO;AAC9C,gBAAQ,MAAM,OAAO,OAAA;AAAA,MACvB,OAAO;AAEL,cAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,OAAO;AACjD,gBAAQ,QAAQ,SAAS,IAAI,CAAC,QAAQ,IAAI,SAAS;AAAA,MACrD;AAGA,YAAM,KAAK,OAAO;AAAA,QAChB,MAAM,QAAQ;AAAA,QACd,SAAS,QAAQ;AAAA,QACjB,MAAM,QAAQ;AAAA,QACd,QAAQ,QAAQ;AAAA,MAAA,CACjB;AAED,cAAQ,KAAK,CAAC;AAAA,IAChB,SAAS,OAAO;AACd,cAAQ,OAAO,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,CAAI;AACzF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AACL;AAKA,SAAS,sBAAsB,SAAwB;AACrD,UACG,QAAQ,QAAQ,EAChB,YAAY,mBAAmB,EAC/B,SAAS,WAAW,cAAc,EAClC,OAAO,UAAU,uBAAuB,EACxC,OAAO,cAAc,2BAA2B,EAChD,OAAO,UAAU,mBAAmB,EACpC,OAAO,YAAY,yBAAyB,EAC5C,OAAO,OAAO,OAAe,YAAY;AACxC,QAAI;AACF,YAAM,aAAa,QAAQ,KAAA;AAC3B,YAAM,SAAS,MAAM,wBAAwB,EAAE,GAAG,YAAY,GAAG,SAAS;AAG1E,YAAM,SAAS,MAAM,oBAAoB,OAAO,SAAS,MAAM;AAC/D,UAAI;AAEJ,UAAI,QAAQ;AAEV,cAAM,SAAS,IAAI,aAAa,OAAO,OAAO;AAC9C,gBAAQ,MAAM,OAAO,OAAA;AAAA,MACvB,OAAO;AAEL,cAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,OAAO;AACjD,gBAAQ,QAAQ,SAAS,IAAI,CAAC,QAAQ,IAAI,SAAS;AAAA,MACrD;AAGA,YAAMC,OAAc,OAAO,OAAO;AAAA,QAChC,MAAM,QAAQ;AAAA,QACd,SAAS,QAAQ;AAAA,QACjB,MAAM,QAAQ;AAAA,QACd,QAAQ,QAAQ;AAAA,MAAA,CACjB;AAED,cAAQ,KAAK,CAAC;AAAA,IAChB,SAAS,OAAO;AACd,cAAQ,OAAO,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,CAAI;AACzF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AACL;AAKA,eAAe,aACb,OACA,QACA,OACe;AACf,QAAM,SAAS,IAAI,aAAa,OAAO,OAAO;AAE9C,aAAW,QAAQ,OAAO;AACxB,QAAI;AACF,YAAM,OAAO,IAAI,IAAI;AACrB,cAAQ,OAAO,MAAM,qBAAqB,KAAK,EAAE;AAAA,CAAK;AAAA,IACxD,SAAS,OAAO;AACd,UAAI,CAAC,SAAS,iBAAiB,SAAS,MAAM,QAAQ,SAAS,WAAW,GAAG;AAC3E,gBAAQ,OAAO,MAAM,UAAU,MAAM,OAAO;AAAA,CAAI;AAChD,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAe,cAAc,OAAkB,aAAqB,OAA+B;AACjG,QAAM,UAAU,MAAM,QAAQ,KAAK,WAAW;AAC9C,QAAM,gBAAgB,QAAQ,OAAA,EAAS,IAAI,CAAC,QAAQ,IAAI,SAAS;AAEjE,aAAW,QAAQ,OAAO;AACxB,UAAM,SAAS,MAAMC,IAAW,eAAe,MAAM,EAAE,OAAO;AAE9D,QAAI,OAAO,OAAO;AAChB,cAAQ,IAAI,OAAO,IAAI;AACvB,cAAQ,OAAO,MAAM,qBAAqB,OAAO,KAAK,EAAE;AAAA,CAAK;AAC7D,oBAAc,KAAK,OAAO,IAAI;AAAA,IAChC,WAAW,OAAO,WAAW;AAC3B,YAAM,IAAI;AAAA,QACR,uBAAuB,OAAO,UAAU,IAAI,mCAAmC,OAAO,UAAU,SAAS,EAAE;AAAA,MAAA;AAAA,IAE/G;AAAA,EACF;AAEA,QAAM,QAAQ,KAAA;AAChB;AAEA,SAAS,eAAe,OAAuB;AAC7C,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,MAAI,QAAQ,SAAS,aAAa,GAAG;AACnC,YAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,QAAQ,SAAS,WAAW,GAAG;AACjC,YAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,UAAQ,KAAK,CAAC;AAChB;AAEA,eAAe,gBACb,MACA,SACA,SACe;AACf,MAAI;AACF,UAAM,aAAa,QAAQ,KAAA;AAC3B,UAAM,SAAS,MAAM,wBAAwB,EAAE,GAAG,YAAY,GAAG,SAAS;AAE1E,UAAM,WAAW,MAAM,cAAc,IAAI;AACzC,UAAM,QAAQ,eAAe,QAAQ;AACrC,UAAM,QAAmB,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAgB;AAEzE,UAAM,SAAS,MAAM,oBAAoB,OAAO,SAAS,MAAM;AAE/D,QAAI,QAAQ;AACV,YAAM,aAAa,OAAO,QAAQ,QAAQ,SAAS,KAAK;AAAA,IAC1D,OAAO;AACL,YAAM,cAAc,OAAO,OAAO,SAAS,QAAQ,SAAS,KAAK;AAAA,IACnE;AAEA,YAAQ,KAAK,CAAC;AAAA,EAChB,SAAS,OAAO;AACd,mBAAe,KAAK;AAAA,EACtB;AACF;AAEA,SAAS,mBAAmB,SAAwB;AAClD,UACG,QAAQ,KAAK,EACb,YAAY,qCAAqC,EACjD,SAAS,UAAU,iCAAiC,EACpD,OAAO,eAAe,0BAA0B,EAChD,OAAO,OAAO,MAA0B,YAAY;AACnD,UAAM,gBAAgB,MAAM,SAAS,OAAO;AAAA,EAC9C,CAAC;AACL;AAKA,eAAe,sBACb,YACA,QACA,QACA,aAC8B;AAC9B,MAAI,QAAQ;AACV,UAAM,SAAS,IAAI,aAAa,OAAO,OAAO;AAC9C,UAAM,QAAQ,MAAM,OAAO,OAAA;AAC3B,WAAO,SACH,MAAM,KAAK,CAAC,SAAS,KAAK,QAAQ,SAAS,UAAU,IACrD,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,UAAU;AAAA,EACjD;AAEA,QAAM,UAAU,MAAM,QAAQ,KAAK,WAAW;AAC9C,QAAM,MAAM,SAAS,QAAQ,WAAW,UAAU,IAAI,QAAQ,SAAS,UAAU;AACjF,SAAO,KAAK,QAAA;AACd;AAEA,eAAe,eAAe,aAAsB,OAAkC;AACpF,MAAI,SAAS,CAAC,SAAS;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,MAAM,QAAQ,YAAY,MAAM,IAC5C,YAAY,OAAO,IAAI,CAAC,MAAM,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,IAAI,IACpF;AACJ,QAAM,aAAa,qBAAqB,YAAY,EAAE;AAAA,SAAc,YAAY,SAAS,YAAY;AAAA,WAAc,OAAO;AAAA;AAE1H,SAAO,MAAM,iBAAiB,UAAU;AAC1C;AAEA,eAAe,gBACb,YACA,aACA,QACA,QACA,aACe;AACf,MAAI,QAAQ;AACV,UAAM,SAAS,IAAI,aAAa,OAAO,OAAO;AAC9C,QAAI,CAAC,YAAY,QAAQ,MAAM;AAC7B,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AACA,UAAM,OAAO,OAAO,YAAY,OAAO,IAAI;AAC3C;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,QAAQ,KAAK,WAAW;AAC9C,QAAM,UAAU,SAAS,QAAQ,aAAa,UAAU,IAAI,QAAQ,WAAW,UAAU;AAEzF,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,qBAAqB;AAAA,EACvC;AAEA,QAAM,QAAQ,KAAA;AAChB;AAEA,SAAS,kBAAkB,OAAuB;AAChD,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,MAAI,QAAQ,SAAS,WAAW,GAAG;AACjC,YAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,UAAQ,KAAK,CAAC;AAChB;AAEA,eAAe,mBACb,YACA,SACA,SACe;AACf,MAAI;AACF,UAAM,aAAa,QAAQ,KAAA;AAC3B,UAAM,SAAS,MAAM,wBAAwB,EAAE,GAAG,YAAY,GAAG,SAAS;AAC1E,UAAM,SAAS,MAAM,oBAAoB,OAAO,SAAS,MAAM;AAE/D,UAAM,cAAc,MAAM;AAAA,MACxB;AAAA,MACA,QAAQ,QAAQ;AAAA,MAChB;AAAA,MACA,OAAO;AAAA,IAAA;AAGT,QAAI,CAAC,aAAa;AAChB,cAAQ,OAAO,MAAM,+BAA+B,UAAU;AAAA,CAAI;AAClE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,YAAY,MAAM,eAAe,aAAa,QAAQ,SAAS,KAAK;AAC1E,QAAI,CAAC,WAAW;AACd,cAAQ,OAAO,MAAM,cAAc;AACnC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,gBAAgB,YAAY,aAAa,QAAQ,QAAQ,OAAO,QAAQ,OAAO,OAAO;AAE5F,YAAQ,OAAO,MAAM,uBAAuB,YAAY,EAAE;AAAA,CAAK;AAC/D,YAAQ,KAAK,CAAC;AAAA,EAChB,SAAS,OAAO;AACd,sBAAkB,KAAK;AAAA,EACzB;AACF;AAEA,SAAS,sBAAsB,SAAwB;AACrD,UACG,QAAQ,QAAQ,EAChB,YAAY,qCAAqC,EACjD,SAAS,gBAAgB,sBAAsB,EAC/C,OAAO,UAAU,8BAA8B,EAC/C,OAAO,eAAe,0BAA0B,EAChD,OAAO,OAAO,YAAoB,YAAY;AAC7C,UAAM,mBAAmB,YAAY,SAAS,OAAO;AAAA,EACvD,CAAC;AACL;AAKA,eAAe,gBACb,YACA,SACA,QACA,QACe;AACf,QAAM,SAAS,IAAI,aAAa,OAAO,OAAO;AAC9C,QAAM,QAAQ,MAAM,OAAO,OAAA;AAC3B,QAAM,cAAc,SAChB,MAAM,KAAK,CAAC,SAAS,KAAK,QAAQ,SAAS,UAAU,IACrD,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,UAAU;AAE/C,MAAI,CAAC,eAAe,CAAC,YAAY,QAAQ,MAAM;AAC7C,YAAQ,OAAO,MAAM,+BAA+B,UAAU;AAAA,CAAI;AAClE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,cAAc,EAAE,GAAG,aAAa,GAAG,QAAA;AACzC,QAAM,OAAO,OAAO,YAAY,OAAO,MAAM,WAAW;AAC1D;AAEA,eAAe,iBACb,YACA,SACA,QACA,aACe;AACf,QAAM,UAAU,MAAM,QAAQ,KAAK,WAAW;AAC9C,QAAM,QAAQ,QAAQ,OAAA,EAAS,IAAI,CAAC,QAAQ,IAAI,SAAS;AAEzD,QAAM,SAAS,MAAMC,OAAc,OAAO,YAAY,SAAS,EAAE,QAAQ;AAEzE,MAAI,CAAC,OAAO,WAAW,CAAC,OAAO,MAAM;AACnC,UAAM,IAAI,MAAM,qBAAqB;AAAA,EACvC;AAEA,MAAI,OAAO,KAAK,QAAQ,MAAM;AAC5B,YAAQ,aAAa,OAAO,KAAK,OAAO,IAAI;AAC5C,YAAQ,IAAI,OAAO,IAAI;AAAA,EACzB;AAEA,QAAM,QAAQ,KAAA;AAChB;AAEA,SAAS,kBAAkB,OAAuB;AAChD,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,MAAI,QAAQ,SAAS,aAAa,GAAG;AACnC,YAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,QAAQ,SAAS,WAAW,KAAK,QAAQ,SAAS,YAAY,GAAG;AACnE,YAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,UAAQ,KAAK,CAAC;AAChB;AAEA,eAAe,mBACb,YACA,MACA,SACA,SACe;AACf,MAAI;AACF,UAAM,aAAa,QAAQ,KAAA;AAC3B,UAAM,SAAS,MAAM,wBAAwB,EAAE,GAAG,YAAY,GAAG,SAAS;AAE1E,UAAM,WAAW,MAAM,cAAc,IAAI;AACzC,UAAM,UAAU,eAAe,QAAQ;AAGvC,UAAM,gBAAgB,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS;AACtD,UAAM,mBAAmB,cAAc,MAAM,OAAO;AAEpD,UAAM,SAAS,MAAM,oBAAoB,OAAO,SAAS,MAAM;AAE/D,QAAI,QAAQ;AACV,YAAM,gBAAgB,YAAY,kBAAkB,QAAQ,QAAQ,OAAO,MAAM;AAAA,IACnF,OAAO;AACL,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,QAAQ,QAAQ;AAAA,QAChB,OAAO;AAAA,MAAA;AAAA,IAEX;AAEA,YAAQ,OAAO,MAAM,uBAAuB,UAAU;AAAA,CAAK;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB,SAAS,OAAO;AACd,sBAAkB,KAAK;AAAA,EACzB;AACF;AAEA,SAAS,sBAAsB,SAAwB;AACrD,UACG,QAAQ,QAAQ,EAChB,YAAY,wCAAwC,EACpD,SAAS,gBAAgB,sBAAsB,EAC/C,SAAS,UAAU,uCAAuC,EAC1D,OAAO,UAAU,8BAA8B,EAC/C,OAAO,OAAO,YAAoB,MAA0B,YAAY;AACvE,UAAM,mBAAmB,YAAY,MAAM,SAAS,OAAO;AAAA,EAC7D,CAAC;AACL;AAKA,SAAS,sBAAsB,SAAwB;AACrD,QAAM,YAAY,QAAQ,QAAQ,QAAQ,EAAE,YAAY,uCAAuC;AAE/F,YACG,QAAQ,OAAO,EACf,YAAY,mBAAmB,EAC/B,OAAO,iBAAiB,qBAAqB,EAC7C,OAAO,gBAAgB,mBAAmB,EAC1C,OAAO,OAAO,YAAY;AACzB,QAAI;AACF,YAAM,aAAa,QAAQ,KAAA;AAC3B,YAAM,SAAS,MAAM,wBAAwB,EAAE,GAAG,YAAY,GAAG,SAAS;AAE1E,YAAM,eAAe,gBAAA;AAErB,YAAM,eAAe;AAAA,QACnB,SAAS,OAAO;AAAA,QAChB;AAAA,QACA,QAAQ,QAAQ;AAAA,QAChB,GAAI,QAAQ,QAAQ,EAAE,MAAM,OAAO,SAAS,QAAQ,MAAM,EAAE,EAAA;AAAA,MAAE;AAGhE,YAAM,YAAY,YAAY;AAE9B,cAAQ,KAAK,CAAC;AAAA,IAChB,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAI,QAAQ,SAAS,iBAAiB,KAAK,QAAQ,SAAS,UAAU,GAAG;AACvE,gBAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,cAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,YACG,QAAQ,MAAM,EACd,YAAY,qBAAqB,EACjC,OAAO,YAAY;AAClB,QAAI;AACF,YAAM,eAAe,gBAAA;AACrB,YAAM,WAAW,YAAY;AAE7B,cAAQ,OAAO,MAAM,mBAAmB;AACxC,cAAQ,KAAK,CAAC;AAAA,IAChB,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAI,QAAQ,SAAS,aAAa,GAAG;AACnC,gBAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,cAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,YACG,QAAQ,QAAQ,EAChB,YAAY,qBAAqB,EACjC,OAAO,YAAY;AAClB,QAAI;AACF,YAAM,eAAe,gBAAA;AACrB,YAAM,SAAS,MAAM,aAAa,YAAY;AAE9C,UAAI,QAAQ;AACV,gBAAQ,OAAO;AAAA,UACb;AAAA,QAA4B,OAAO,IAAI;AAAA,OAAU,OAAO,GAAG;AAAA,WAAc,OAAO,OAAO;AAAA;AAAA,QAAA;AAEzF,gBAAQ,KAAK,CAAC;AAAA,MAChB,OAAO;AACL,gBAAQ,OAAO,MAAM,sBAAsB;AAC3C,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,OAAO,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,CAAI;AACzF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AACL;AAKA,eAAsB,KAAK,MAA+B;AACxD,QAAM,UAAU,cAAA;AAGhB,UAAQ,GAAG,UAAU,MAAM;AACzB,YAAQ,KAAK,GAAG;AAAA,EAClB,CAAC;AAED,UAAQ,GAAG,WAAW,MAAM;AAC1B,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AAED,QAAM,QAAQ,WAAW,IAAI;AAC/B;"}
1
+ {"version":3,"file":"cli.js","sources":["../src/server/portfile.ts","../src/cli/commands/add.ts","../src/cli/commands/cite.ts","../src/features/fulltext/types.ts","../src/features/fulltext/filename.ts","../src/features/fulltext/manager.ts","../src/cli/commands/fulltext.ts","../src/cli/commands/list.ts","../src/cli/commands/remove.ts","../src/cli/commands/search.ts","../src/cli/commands/server.ts","../src/cli/commands/update.ts","../src/cli/server-client.ts","../src/cli/server-detection.ts","../src/cli/execution-context.ts","../src/cli/helpers.ts","../src/cli/index.ts"],"sourcesContent":["import { promises as fs } from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\n\n/**\n * Get the default portfile path.\n * @returns The path to the portfile in the system's temp directory.\n */\nexport function getPortfilePath(): string {\n const tmpDir = os.tmpdir();\n return path.join(tmpDir, \"reference-manager\", \"server.port\");\n}\n\n/**\n * Write port, PID, library path, and optionally started_at to the portfile.\n * @param portfilePath - Path to the portfile\n * @param port - Server port number\n * @param pid - Server process ID\n * @param library - Path to the library file\n * @param started_at - Optional ISO 8601 timestamp of when the server started\n */\nexport async function writePortfile(\n portfilePath: string,\n port: number,\n pid: number,\n library: string,\n started_at?: string\n): Promise<void> {\n // Create parent directory if it doesn't exist\n const dir = path.dirname(portfilePath);\n await fs.mkdir(dir, { recursive: true });\n\n // Write portfile with port, pid, library, and optionally started_at\n const data: Record<string, unknown> = { port, pid, library };\n if (started_at !== undefined) {\n data.started_at = started_at;\n }\n const content = JSON.stringify(data, null, 2);\n await fs.writeFile(portfilePath, content, \"utf-8\");\n}\n\n/**\n * Read port, PID, library, and optionally started_at from the portfile.\n * @param portfilePath - Path to the portfile\n * @returns Object with port, pid, library (if present), and started_at (if present), or null if file doesn't exist or is invalid\n */\nexport async function readPortfile(portfilePath: string): Promise<{\n port: number;\n pid: number;\n library?: string;\n started_at?: string;\n} | null> {\n try {\n const content = await fs.readFile(portfilePath, \"utf-8\");\n const data = JSON.parse(content);\n\n // Validate required fields (port and pid are always required)\n if (typeof data.port !== \"number\" || typeof data.pid !== \"number\") {\n return null;\n }\n\n // Build result with required fields\n const result: {\n port: number;\n pid: number;\n library?: string;\n started_at?: string;\n } = {\n port: data.port,\n pid: data.pid,\n };\n\n // Add optional fields if present\n if (typeof data.library === \"string\") {\n result.library = data.library;\n }\n if (typeof data.started_at === \"string\") {\n result.started_at = data.started_at;\n }\n\n return result;\n } catch {\n // File doesn't exist or invalid JSON\n return null;\n }\n}\n\n/**\n * Check if the portfile exists.\n * @param portfilePath - Path to the portfile\n * @returns True if portfile exists, false otherwise\n */\nexport async function portfileExists(portfilePath: string): Promise<boolean> {\n try {\n await fs.access(portfilePath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove the portfile.\n * @param portfilePath - Path to the portfile\n */\nexport async function removePortfile(portfilePath: string): Promise<void> {\n try {\n await fs.unlink(portfilePath);\n } catch {\n // Ignore if file doesn't exist\n }\n}\n\n/**\n * Check if a process with the given PID is running.\n * @param pid - Process ID to check\n * @returns True if process is running, false otherwise\n */\nexport function isProcessRunning(pid: number): boolean {\n if (pid <= 0) {\n return false;\n }\n\n try {\n // Sending signal 0 checks if the process exists without killing it\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n","import type { InputFormat } from \"../../features/import/detector.js\";\nimport type { PubmedConfig } from \"../../features/import/fetcher.js\";\nimport {\n type AddReferencesOptions,\n type AddReferencesResult,\n type AddedItem,\n type FailedItem,\n type SkippedItem,\n addReferences,\n} from \"../../features/operations/add.js\";\nimport type { ExecutionContext } from \"../execution-context.js\";\n\n/**\n * Options for the add command.\n */\nexport interface AddCommandOptions {\n /** Input strings (file paths, PMIDs, DOIs) */\n inputs: string[];\n /** Skip duplicate detection */\n force: boolean;\n /** Explicit input format */\n format?: string;\n /** PubMed API configuration */\n pubmedConfig?: PubmedConfig;\n /** Show verbose error output */\n verbose?: boolean;\n /** Content from stdin */\n stdinContent?: string;\n}\n\n/**\n * Result from add command execution.\n */\nexport type AddCommandResult = AddReferencesResult;\n\n// Re-export types for convenience\nexport type { AddedItem, FailedItem, SkippedItem };\n\n/** Maximum error message length in non-verbose mode */\nconst MAX_ERROR_LENGTH = 80;\n\n/**\n * Execute add command.\n * Routes to server API or direct library operation based on execution context.\n *\n * @param options - Add command options\n * @param context - Execution context (server or local)\n * @returns Add result containing added, failed, and skipped items\n */\nexport async function executeAdd(\n options: AddCommandOptions,\n context: ExecutionContext\n): Promise<AddCommandResult> {\n const { inputs, force, format, pubmedConfig, stdinContent } = options;\n\n if (context.type === \"server\") {\n // Route through server - build options without undefined values\n const serverOptions: { force: boolean; format?: string; stdinContent?: string } = { force };\n if (format !== undefined) {\n serverOptions.format = format;\n }\n if (stdinContent !== undefined) {\n serverOptions.stdinContent = stdinContent;\n }\n return context.client.addFromInputs(inputs, serverOptions);\n }\n\n // Direct library operation - build options without undefined values\n const addOptions: AddReferencesOptions = { force };\n if (format !== undefined) {\n addOptions.format = format as InputFormat | \"auto\";\n }\n if (pubmedConfig !== undefined) {\n addOptions.pubmedConfig = pubmedConfig;\n }\n if (stdinContent !== undefined) {\n addOptions.stdinContent = stdinContent;\n }\n\n return addReferences(inputs, context.library, addOptions);\n}\n\n/**\n * Format a single added item for output.\n */\nfunction formatAddedItem(item: AddedItem): string {\n const idPart = item.idChanged ? `${item.id} (was: ${item.originalId})` : item.id;\n const title = item.title ?? \"(no title)\";\n return ` - ${idPart}: \"${title}\"`;\n}\n\n/**\n * Format a single failed item for output.\n */\nfunction formatFailedItem(item: FailedItem, verbose: boolean): string {\n let error = item.error;\n if (!verbose && error.length > MAX_ERROR_LENGTH) {\n error = `${error.substring(0, MAX_ERROR_LENGTH - 3)}...`;\n }\n // In verbose mode, preserve multi-line errors; in non-verbose mode, take first line\n if (!verbose && error.includes(\"\\n\")) {\n error = error.split(\"\\n\")[0] ?? error;\n }\n return ` - ${item.source}: ${error}`;\n}\n\n/**\n * Format a single skipped item for output.\n */\nfunction formatSkippedItem(item: SkippedItem): string {\n return ` - ${item.source}: matches existing '${item.existingId}'`;\n}\n\n/**\n * Format add result for CLI output.\n *\n * @param result - Add result\n * @param verbose - Show verbose error output\n * @returns Formatted output string\n */\nexport function formatAddOutput(result: AddCommandResult, verbose: boolean): string {\n const lines: string[] = [];\n\n // Added section\n if (result.added.length > 0) {\n lines.push(`Added ${result.added.length} reference(s):`);\n for (const item of result.added) {\n lines.push(formatAddedItem(item));\n }\n } else if (result.failed.length === 0 && result.skipped.length === 0) {\n lines.push(\"Added 0 reference(s).\");\n }\n\n // Failed section\n if (result.failed.length > 0) {\n if (lines.length > 0) lines.push(\"\");\n lines.push(`Failed to add ${result.failed.length} item(s):`);\n for (const item of result.failed) {\n lines.push(formatFailedItem(item, verbose));\n }\n }\n\n // Skipped section\n if (result.skipped.length > 0) {\n if (lines.length > 0) lines.push(\"\");\n lines.push(`Skipped ${result.skipped.length} duplicate(s):`);\n for (const item of result.skipped) {\n lines.push(formatSkippedItem(item));\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Determine exit code based on result.\n *\n * @param result - Add result\n * @returns Exit code (0 for success/partial success, 1 for complete failure)\n */\nexport function getExitCode(result: AddCommandResult): number {\n // Success if anything was added\n if (result.added.length > 0) {\n return 0;\n }\n\n // Complete failure if nothing added and there were failures\n if (result.failed.length > 0) {\n return 1;\n }\n\n // All skipped or empty input is considered success\n return 0;\n}\n","import { type CiteResult, citeReferences } from \"../../features/operations/cite.js\";\nimport type { ExecutionContext } from \"../execution-context.js\";\nimport type { ServerClient } from \"../server-client.js\";\n\n/**\n * Options for the cite command.\n */\nexport interface CiteCommandOptions {\n identifiers: string[];\n uuid?: boolean;\n style?: string;\n cslFile?: string;\n locale?: string;\n format?: \"text\" | \"html\" | \"rtf\";\n inText?: boolean;\n}\n\n/**\n * Result from cite command execution.\n */\nexport type CiteCommandResult = CiteResult;\n\n/**\n * Validate citation options.\n */\nasync function validateOptions(options: CiteCommandOptions): Promise<void> {\n if (options.format && ![\"text\", \"html\", \"rtf\"].includes(options.format)) {\n throw new Error(`Invalid format '${options.format}'. Must be one of: text, html, rtf`);\n }\n\n if (options.cslFile) {\n const fs = await import(\"node:fs\");\n if (!fs.existsSync(options.cslFile)) {\n throw new Error(`CSL file '${options.cslFile}' not found`);\n }\n }\n}\n\n/**\n * Build server cite options from command options.\n */\nfunction buildServerCiteOptions(options: CiteCommandOptions): Parameters<ServerClient[\"cite\"]>[0] {\n return {\n identifiers: options.identifiers,\n ...(options.uuid !== undefined && { byUuid: options.uuid }),\n ...(options.inText !== undefined && { inText: options.inText }),\n ...(options.style !== undefined && { style: options.style }),\n ...(options.cslFile !== undefined && { cslFile: options.cslFile }),\n ...(options.locale !== undefined && { locale: options.locale }),\n ...(options.format !== undefined && {\n format: options.format === \"rtf\" ? \"text\" : options.format,\n }),\n };\n}\n\n/**\n * Build operation cite options from command options.\n */\nfunction buildOperationCiteOptions(\n options: CiteCommandOptions\n): Parameters<typeof citeReferences>[1] {\n return {\n identifiers: options.identifiers,\n ...(options.uuid !== undefined && { byUuid: options.uuid }),\n ...(options.style !== undefined && { style: options.style }),\n ...(options.cslFile !== undefined && { cslFile: options.cslFile }),\n ...(options.locale !== undefined && { locale: options.locale }),\n ...(options.format !== undefined && { format: options.format }),\n ...(options.inText !== undefined && { inText: options.inText }),\n };\n}\n\n/**\n * Execute cite command.\n * Routes to server API or direct library operation based on execution context.\n *\n * @param options - Cite command options\n * @param context - Execution context (server or local)\n * @returns Cite result containing per-identifier results\n */\nexport async function executeCite(\n options: CiteCommandOptions,\n context: ExecutionContext\n): Promise<CiteCommandResult> {\n await validateOptions(options);\n\n if (context.type === \"server\") {\n return context.client.cite(buildServerCiteOptions(options));\n }\n\n return citeReferences(context.library, buildOperationCiteOptions(options));\n}\n\n/**\n * Format cite result for CLI output.\n *\n * @param result - Cite result\n * @returns Formatted output string (for stdout)\n */\nexport function formatCiteOutput(result: CiteCommandResult): string {\n const lines: string[] = [];\n for (const r of result.results) {\n if (r.success) {\n lines.push(r.citation);\n }\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Format cite errors for stderr.\n *\n * @param result - Cite result\n * @returns Error messages to write to stderr\n */\nexport function formatCiteErrors(result: CiteCommandResult): string {\n const lines: string[] = [];\n for (const r of result.results) {\n if (!r.success) {\n lines.push(`Error for '${r.identifier}': ${r.error}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Determine exit code based on result.\n *\n * @param result - Cite result\n * @returns Exit code (0 for success/partial success, 1 for complete failure)\n */\nexport function getCiteExitCode(result: CiteCommandResult): number {\n const hasSuccess = result.results.some((r) => r.success);\n const hasError = result.results.some((r) => !r.success);\n\n if (hasSuccess) {\n return 0;\n }\n if (hasError) {\n return 1;\n }\n return 0;\n}\n","/**\n * Fulltext management types\n */\n\n/**\n * Supported fulltext file types\n */\nexport type FulltextType = \"pdf\" | \"markdown\";\n\n/**\n * File extensions for each fulltext type\n */\nexport const FULLTEXT_EXTENSIONS: Record<FulltextType, string> = {\n pdf: \".pdf\",\n markdown: \".md\",\n};\n","/**\n * Fulltext filename generation\n */\n\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport { FULLTEXT_EXTENSIONS, type FulltextType } from \"./types.js\";\n\n/**\n * Generate a filename for a fulltext file.\n *\n * Format: {id}[-PMID{PMID}]-{uuid}.{ext}\n *\n * @param item - CSL item to generate filename for\n * @param type - Fulltext type (pdf or markdown)\n * @returns Generated filename\n * @throws Error if custom.uuid is missing\n */\nexport function generateFulltextFilename(item: CslItem, type: FulltextType): string {\n const uuid = item.custom?.uuid;\n if (!uuid) {\n throw new Error(\"Missing uuid in custom field\");\n }\n\n const parts: string[] = [item.id];\n\n // Add PMID if present and non-empty\n if (item.PMID && item.PMID.length > 0) {\n parts.push(`PMID${item.PMID}`);\n }\n\n // Add UUID\n parts.push(uuid);\n\n // Join with hyphens and add extension\n return parts.join(\"-\") + FULLTEXT_EXTENSIONS[type];\n}\n","/**\n * Fulltext file management\n */\n\nimport { existsSync } from \"node:fs\";\nimport { copyFile, mkdir, rename, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport { generateFulltextFilename } from \"./filename.js\";\nimport type { FulltextType } from \"./types.js\";\n\n/**\n * Error thrown when fulltext I/O operation fails\n */\nexport class FulltextIOError extends Error {\n constructor(\n message: string,\n public readonly cause?: Error\n ) {\n super(message);\n this.name = \"FulltextIOError\";\n }\n}\n\n/**\n * Error thrown when trying to detach non-attached fulltext\n */\nexport class FulltextNotAttachedError extends Error {\n constructor(\n public readonly itemId: string,\n public readonly type: FulltextType\n ) {\n super(`No ${type} attached to reference ${itemId}`);\n this.name = \"FulltextNotAttachedError\";\n }\n}\n\n/**\n * Options for attachFile\n */\nexport interface AttachOptions {\n /** Move file instead of copy (default: false) */\n move?: boolean;\n /** Overwrite existing attachment without confirmation (default: false) */\n force?: boolean;\n}\n\n/**\n * Result of attachFile operation\n */\nexport interface AttachResult {\n /** Generated filename */\n filename: string;\n /** Existing filename if already attached (when force=false) */\n existingFile?: string;\n /** Whether existing file was overwritten */\n overwritten: boolean;\n /** Old filename that was deleted (when force=true and filename changed) */\n deletedOldFile?: string;\n}\n\n/**\n * Options for detachFile\n */\nexport interface DetachOptions {\n /** Delete file from disk (default: false, metadata-only detach) */\n delete?: boolean;\n}\n\n/**\n * Result of detachFile operation\n */\nexport interface DetachResult {\n /** Detached filename */\n filename: string;\n /** Whether file was deleted from disk */\n deleted: boolean;\n}\n\n/**\n * Manages fulltext file operations\n */\nexport class FulltextManager {\n constructor(private readonly fulltextDirectory: string) {}\n\n /**\n * Ensure the fulltext directory exists\n */\n async ensureDirectory(): Promise<void> {\n await mkdir(this.fulltextDirectory, { recursive: true });\n }\n\n /**\n * Attach a file to a reference\n */\n async attachFile(\n item: CslItem,\n sourcePath: string,\n type: FulltextType,\n options?: AttachOptions\n ): Promise<AttachResult> {\n const { move = false, force = false } = options ?? {};\n\n // Generate new filename based on current item state\n const newFilename = generateFulltextFilename(item, type);\n\n // Validate source file exists\n this.validateSourceFile(sourcePath);\n\n // Get existing filename from metadata\n const existingFilename = this.getExistingFilename(item, type);\n\n // If already attached and not force, return existing info\n if (existingFilename && !force) {\n return {\n filename: newFilename,\n existingFile: existingFilename,\n overwritten: false,\n };\n }\n\n // Ensure directory exists\n await this.ensureDirectory();\n\n // Delete old file if force and filename changed\n const deletedOldFile = await this.deleteOldFileIfNeeded(existingFilename, newFilename, force);\n\n // Copy or move file to destination\n const destPath = join(this.fulltextDirectory, newFilename);\n await this.copyOrMoveFile(sourcePath, destPath, move);\n\n const result: AttachResult = {\n filename: newFilename,\n overwritten: existingFilename !== undefined,\n };\n if (deletedOldFile) {\n result.deletedOldFile = deletedOldFile;\n }\n return result;\n }\n\n /**\n * Validate that source file exists\n */\n private validateSourceFile(sourcePath: string): void {\n if (!existsSync(sourcePath)) {\n throw new FulltextIOError(`Source file not found: ${sourcePath}`);\n }\n }\n\n /**\n * Delete old file if force mode and filename changed\n * @returns Deleted filename or undefined\n */\n private async deleteOldFileIfNeeded(\n existingFilename: string | undefined,\n newFilename: string,\n force: boolean\n ): Promise<string | undefined> {\n if (!force || !existingFilename || existingFilename === newFilename) {\n return undefined;\n }\n\n const oldPath = join(this.fulltextDirectory, existingFilename);\n try {\n await unlink(oldPath);\n } catch {\n // Ignore if old file doesn't exist\n }\n return existingFilename;\n }\n\n /**\n * Copy or move file to destination\n */\n private async copyOrMoveFile(sourcePath: string, destPath: string, move: boolean): Promise<void> {\n try {\n if (move) {\n await rename(sourcePath, destPath);\n } else {\n await copyFile(sourcePath, destPath);\n }\n } catch (error) {\n const operation = move ? \"move\" : \"copy\";\n throw new FulltextIOError(\n `Failed to ${operation} file to ${destPath}`,\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Get the full path for an attached file\n * @returns Full path or null if not attached\n */\n getFilePath(item: CslItem, type: FulltextType): string | null {\n const filename = this.getExistingFilename(item, type);\n if (!filename) {\n return null;\n }\n return join(this.fulltextDirectory, filename);\n }\n\n /**\n * Detach a file from a reference\n */\n async detachFile(\n item: CslItem,\n type: FulltextType,\n options?: DetachOptions\n ): Promise<DetachResult> {\n const { delete: deleteFile = false } = options ?? {};\n\n const filename = this.getExistingFilename(item, type);\n if (!filename) {\n throw new FulltextNotAttachedError(item.id, type);\n }\n\n if (deleteFile) {\n const filePath = join(this.fulltextDirectory, filename);\n try {\n await unlink(filePath);\n } catch {\n // Ignore if file doesn't exist (orphaned metadata)\n }\n }\n\n return {\n filename,\n deleted: deleteFile,\n };\n }\n\n /**\n * Get list of attached fulltext types\n */\n getAttachedTypes(item: CslItem): FulltextType[] {\n const types: FulltextType[] = [];\n const fulltext = item.custom?.fulltext;\n\n if (fulltext?.pdf) {\n types.push(\"pdf\");\n }\n if (fulltext?.markdown) {\n types.push(\"markdown\");\n }\n\n return types;\n }\n\n /**\n * Check if item has attachment\n * @param type Optional type to check; if omitted, checks for any attachment\n */\n hasAttachment(item: CslItem, type?: FulltextType): boolean {\n if (type) {\n return this.getExistingFilename(item, type) !== undefined;\n }\n return this.getAttachedTypes(item).length > 0;\n }\n\n /**\n * Get existing filename from item metadata\n */\n private getExistingFilename(item: CslItem, type: FulltextType): string | undefined {\n const fulltext = item.custom?.fulltext;\n if (!fulltext) {\n return undefined;\n }\n return fulltext[type];\n }\n}\n","/**\n * Fulltext CLI commands: attach, get, detach\n */\n\nimport { mkdtempSync, writeFileSync } from \"node:fs\";\nimport { readFile, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { extname, join } from \"node:path\";\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport {\n FulltextIOError,\n FulltextManager,\n FulltextNotAttachedError,\n type FulltextType,\n} from \"../../features/fulltext/index.js\";\nimport { updateReference } from \"../../features/operations/update.js\";\nimport type { ExecutionContext } from \"../execution-context.js\";\n\n/**\n * Options for fulltext attach command\n */\nexport interface FulltextAttachOptions {\n identifier: string;\n filePath?: string;\n type?: FulltextType;\n move?: boolean;\n force?: boolean;\n byUuid?: boolean;\n fulltextDirectory: string;\n // For stdin support\n stdinContent?: Buffer;\n}\n\n/**\n * Result from fulltext attach command\n */\nexport interface FulltextAttachResult {\n success: boolean;\n filename?: string;\n type?: FulltextType;\n overwritten?: boolean;\n existingFile?: string;\n requiresConfirmation?: boolean;\n error?: string;\n}\n\n/**\n * Options for fulltext get command\n */\nexport interface FulltextGetOptions {\n identifier: string;\n type?: FulltextType;\n stdout?: boolean;\n byUuid?: boolean;\n fulltextDirectory: string;\n}\n\n/**\n * Result from fulltext get command\n */\nexport interface FulltextGetResult {\n success: boolean;\n paths?: {\n pdf?: string;\n markdown?: string;\n };\n content?: Buffer;\n error?: string;\n}\n\n/**\n * Options for fulltext detach command\n */\nexport interface FulltextDetachOptions {\n identifier: string;\n type?: FulltextType;\n delete?: boolean;\n force?: boolean;\n byUuid?: boolean;\n fulltextDirectory: string;\n}\n\n/**\n * Result from fulltext detach command\n */\nexport interface FulltextDetachResult {\n success: boolean;\n detached?: FulltextType[];\n deleted?: FulltextType[];\n error?: string;\n}\n\n/**\n * Detect fulltext type from file extension\n */\nfunction detectType(filePath: string): FulltextType | undefined {\n const ext = extname(filePath).toLowerCase();\n if (ext === \".pdf\") return \"pdf\";\n if (ext === \".md\" || ext === \".markdown\") return \"markdown\";\n return undefined;\n}\n\n/**\n * Find reference by identifier\n */\nasync function findReference(\n identifier: string,\n context: ExecutionContext,\n byUuid: boolean\n): Promise<CslItem | null> {\n if (context.type === \"server\") {\n return context.client.find(identifier, { byUuid });\n }\n const ref = byUuid\n ? context.library.findByUuid(identifier)\n : context.library.findById(identifier);\n return ref?.getItem() ?? null;\n}\n\n/**\n * Update reference metadata with fulltext info\n */\nasync function updateFulltextMetadata(\n identifier: string,\n fulltext: { pdf?: string; markdown?: string } | undefined,\n context: ExecutionContext,\n byUuid: boolean\n): Promise<void> {\n // Build updates with fulltext only - other custom fields preserved by merge in updateReference\n const updates = {\n custom: { fulltext },\n } as Partial<CslItem>;\n\n if (context.type === \"server\") {\n await context.client.update(identifier, updates, { byUuid });\n } else {\n await updateReference(context.library, {\n identifier,\n updates,\n byUuid,\n });\n }\n}\n\n/**\n * Clean up temp directory\n */\nasync function cleanupTempDir(tempDir: string | undefined): Promise<void> {\n if (tempDir) {\n await rm(tempDir, { recursive: true, force: true }).catch(() => {});\n }\n}\n\n/**\n * Prepare source path from stdin content\n */\nfunction prepareStdinSource(\n stdinContent: Buffer,\n fileType: FulltextType\n): { sourcePath: string; tempDir: string } | { error: string } {\n try {\n const tempDir = mkdtempSync(join(tmpdir(), \"refmgr-\"));\n const ext = fileType === \"pdf\" ? \".pdf\" : \".md\";\n const sourcePath = join(tempDir, `stdin${ext}`);\n writeFileSync(sourcePath, stdinContent);\n return { sourcePath, tempDir };\n } catch (error) {\n return {\n error: `Failed to write stdin content: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n\n/**\n * Build new fulltext metadata\n */\nfunction buildNewFulltext(\n currentFulltext: { pdf?: string | undefined; markdown?: string | undefined },\n fileType: FulltextType,\n filename: string\n): { pdf?: string; markdown?: string } {\n const newFulltext: { pdf?: string; markdown?: string } = {};\n if (currentFulltext.pdf) newFulltext.pdf = currentFulltext.pdf;\n if (currentFulltext.markdown) newFulltext.markdown = currentFulltext.markdown;\n newFulltext[fileType] = filename;\n return newFulltext;\n}\n\n/**\n * Resolve file type from options\n */\nfunction resolveFileType(\n explicitType: FulltextType | undefined,\n filePath: string | undefined,\n stdinContent: Buffer | undefined\n): FulltextType | { error: string } {\n let fileType = explicitType;\n if (!fileType && filePath) {\n fileType = detectType(filePath);\n }\n\n if (stdinContent && !fileType) {\n return {\n error: \"File type must be specified with --pdf or --markdown when reading from stdin.\",\n };\n }\n\n if (!fileType) {\n return { error: \"Cannot detect file type. Use --pdf or --markdown to specify the type.\" };\n }\n\n return fileType;\n}\n\n/**\n * Execute fulltext attach command\n */\nexport async function executeFulltextAttach(\n options: FulltextAttachOptions,\n context: ExecutionContext\n): Promise<FulltextAttachResult> {\n const {\n identifier,\n filePath,\n type: explicitType,\n move,\n force,\n byUuid = false,\n fulltextDirectory,\n stdinContent,\n } = options;\n\n // Find reference\n const item = await findReference(identifier, context, byUuid);\n if (!item) {\n return { success: false, error: `Reference '${identifier}' not found` };\n }\n\n // Resolve file type\n const fileTypeResult = resolveFileType(explicitType, filePath, stdinContent);\n if (typeof fileTypeResult === \"object\" && \"error\" in fileTypeResult) {\n return { success: false, error: fileTypeResult.error };\n }\n const fileType = fileTypeResult;\n\n // Prepare source path\n let sourcePath = filePath;\n let tempDir: string | undefined;\n\n if (stdinContent) {\n const stdinResult = prepareStdinSource(stdinContent, fileType);\n if (\"error\" in stdinResult) {\n return { success: false, error: stdinResult.error };\n }\n sourcePath = stdinResult.sourcePath;\n tempDir = stdinResult.tempDir;\n }\n\n if (!sourcePath) {\n return { success: false, error: \"No file path or stdin content provided.\" };\n }\n\n // Attach file\n const manager = new FulltextManager(fulltextDirectory);\n\n try {\n const attachOptions = {\n ...(move !== undefined && { move }),\n ...(force !== undefined && { force }),\n };\n const result = await manager.attachFile(item, sourcePath, fileType, attachOptions);\n\n // If existing file and not force, return confirmation required\n if (result.existingFile && !result.overwritten) {\n await cleanupTempDir(tempDir);\n return { success: false, existingFile: result.existingFile, requiresConfirmation: true };\n }\n\n // Update metadata\n const newFulltext = buildNewFulltext(item.custom?.fulltext ?? {}, fileType, result.filename);\n await updateFulltextMetadata(identifier, newFulltext, context, byUuid);\n await cleanupTempDir(tempDir);\n\n return {\n success: true,\n filename: result.filename,\n type: fileType,\n overwritten: result.overwritten,\n };\n } catch (error) {\n await cleanupTempDir(tempDir);\n if (error instanceof FulltextIOError) {\n return { success: false, error: error.message };\n }\n throw error;\n }\n}\n\n/**\n * Get file content for stdout mode\n */\nasync function getFileContent(\n manager: FulltextManager,\n item: CslItem,\n type: FulltextType,\n identifier: string\n): Promise<FulltextGetResult> {\n const filePath = manager.getFilePath(item, type);\n if (!filePath) {\n return { success: false, error: `No ${type} fulltext attached to '${identifier}'` };\n }\n\n try {\n const content = await readFile(filePath);\n return { success: true, content };\n } catch (error) {\n return {\n success: false,\n error: `Failed to read file: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n\n/**\n * Get file paths for path mode\n */\nfunction getFilePaths(\n manager: FulltextManager,\n item: CslItem,\n types: FulltextType[],\n identifier: string\n): FulltextGetResult {\n const paths: { pdf?: string; markdown?: string } = {};\n for (const t of types) {\n const filePath = manager.getFilePath(item, t);\n if (filePath) {\n paths[t] = filePath;\n }\n }\n\n if (Object.keys(paths).length === 0) {\n return { success: false, error: `No fulltext attached to '${identifier}'` };\n }\n\n return { success: true, paths };\n}\n\n/**\n * Execute fulltext get command\n */\nexport async function executeFulltextGet(\n options: FulltextGetOptions,\n context: ExecutionContext\n): Promise<FulltextGetResult> {\n const { identifier, type, stdout, byUuid = false, fulltextDirectory } = options;\n\n const item = await findReference(identifier, context, byUuid);\n if (!item) {\n return { success: false, error: `Reference '${identifier}' not found` };\n }\n\n const manager = new FulltextManager(fulltextDirectory);\n\n // Stdout mode with specific type\n if (stdout && type) {\n return getFileContent(manager, item, type, identifier);\n }\n\n // Path mode\n const attachedTypes = type ? [type] : manager.getAttachedTypes(item);\n if (attachedTypes.length === 0) {\n return { success: false, error: `No fulltext attached to '${identifier}'` };\n }\n\n return getFilePaths(manager, item, attachedTypes, identifier);\n}\n\n/**\n * Perform detach operations for specified types\n */\nasync function performDetachOperations(\n manager: FulltextManager,\n item: CslItem,\n typesToDetach: FulltextType[],\n deleteFile: boolean | undefined\n): Promise<{ detached: FulltextType[]; deleted: FulltextType[] }> {\n const detached: FulltextType[] = [];\n const deleted: FulltextType[] = [];\n\n for (const t of typesToDetach) {\n const detachOptions = deleteFile ? { delete: deleteFile } : {};\n const result = await manager.detachFile(item, t, detachOptions);\n detached.push(t);\n if (result.deleted) {\n deleted.push(t);\n }\n }\n\n return { detached, deleted };\n}\n\n/**\n * Build remaining fulltext metadata after detach\n */\nfunction buildRemainingFulltext(\n currentFulltext: { pdf?: string | undefined; markdown?: string | undefined },\n detached: FulltextType[]\n): { pdf?: string; markdown?: string } | undefined {\n const newFulltext: { pdf?: string; markdown?: string } = {};\n if (currentFulltext.pdf && !detached.includes(\"pdf\")) {\n newFulltext.pdf = currentFulltext.pdf;\n }\n if (currentFulltext.markdown && !detached.includes(\"markdown\")) {\n newFulltext.markdown = currentFulltext.markdown;\n }\n return Object.keys(newFulltext).length > 0 ? newFulltext : undefined;\n}\n\n/**\n * Handle detach errors\n */\nfunction handleDetachError(error: unknown): FulltextDetachResult {\n if (error instanceof FulltextNotAttachedError || error instanceof FulltextIOError) {\n return { success: false, error: error.message };\n }\n throw error;\n}\n\n/**\n * Execute fulltext detach command\n */\nexport async function executeFulltextDetach(\n options: FulltextDetachOptions,\n context: ExecutionContext\n): Promise<FulltextDetachResult> {\n const { identifier, type, delete: deleteFile, byUuid = false, fulltextDirectory } = options;\n\n const item = await findReference(identifier, context, byUuid);\n if (!item) {\n return { success: false, error: `Reference '${identifier}' not found` };\n }\n\n const manager = new FulltextManager(fulltextDirectory);\n const typesToDetach: FulltextType[] = type ? [type] : manager.getAttachedTypes(item);\n\n if (typesToDetach.length === 0) {\n return { success: false, error: `No fulltext attached to '${identifier}'` };\n }\n\n try {\n const { detached, deleted } = await performDetachOperations(\n manager,\n item,\n typesToDetach,\n deleteFile\n );\n\n const updatedFulltext = buildRemainingFulltext(item.custom?.fulltext ?? {}, detached);\n await updateFulltextMetadata(identifier, updatedFulltext, context, byUuid);\n\n const resultData: FulltextDetachResult = { success: true, detached };\n if (deleted.length > 0) {\n resultData.deleted = deleted;\n }\n return resultData;\n } catch (error) {\n return handleDetachError(error);\n }\n}\n\n/**\n * Format fulltext attach output\n */\nexport function formatFulltextAttachOutput(result: FulltextAttachResult): string {\n if (result.requiresConfirmation) {\n return `File already attached: ${result.existingFile}\\nUse --force to overwrite.`;\n }\n\n if (!result.success) {\n return `Error: ${result.error}`;\n }\n\n const parts: string[] = [];\n if (result.overwritten) {\n parts.push(`Attached ${result.type} (overwritten): ${result.filename}`);\n } else {\n parts.push(`Attached ${result.type}: ${result.filename}`);\n }\n\n return parts.join(\"\\n\");\n}\n\n/**\n * Format fulltext get output\n */\nexport function formatFulltextGetOutput(result: FulltextGetResult): string {\n if (!result.success) {\n return `Error: ${result.error}`;\n }\n\n // If content mode (stdout), the content is returned as Buffer, handled by CLI\n if (result.content) {\n return result.content.toString();\n }\n\n // Path mode\n const lines: string[] = [];\n if (result.paths?.pdf) {\n lines.push(`pdf: ${result.paths.pdf}`);\n }\n if (result.paths?.markdown) {\n lines.push(`markdown: ${result.paths.markdown}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format fulltext detach output\n */\nexport function formatFulltextDetachOutput(result: FulltextDetachResult): string {\n if (!result.success) {\n return `Error: ${result.error}`;\n }\n\n const lines: string[] = [];\n for (const type of result.detached ?? []) {\n if (result.deleted?.includes(type)) {\n lines.push(`Detached and deleted ${type}`);\n } else {\n lines.push(`Detached ${type}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Get exit code for fulltext command result\n */\nexport function getFulltextExitCode(\n result: FulltextAttachResult | FulltextGetResult | FulltextDetachResult\n): number {\n return result.success ? 0 : 1;\n}\n","import {\n type ListFormat,\n type ListResult,\n listReferences,\n} from \"../../features/operations/list.js\";\nimport type { ExecutionContext } from \"../execution-context.js\";\n\n/**\n * Options for the list command.\n */\nexport interface ListCommandOptions {\n json?: boolean;\n idsOnly?: boolean;\n uuid?: boolean;\n bibtex?: boolean;\n}\n\n/**\n * Result from list command execution.\n */\nexport type ListCommandResult = ListResult;\n\n/**\n * Convert CLI options to ListFormat.\n */\nfunction getListFormat(options: ListCommandOptions): ListFormat {\n if (options.json) return \"json\";\n if (options.idsOnly) return \"ids-only\";\n if (options.uuid) return \"uuid\";\n if (options.bibtex) return \"bibtex\";\n return \"pretty\";\n}\n\n/**\n * Validate that only one output format is specified.\n */\nfunction validateOptions(options: ListCommandOptions): void {\n const outputOptions = [options.json, options.idsOnly, options.uuid, options.bibtex].filter(\n Boolean\n );\n\n if (outputOptions.length > 1) {\n throw new Error(\n \"Multiple output formats specified. Only one of --json, --ids-only, --uuid, --bibtex can be used.\"\n );\n }\n}\n\n/**\n * Execute list command.\n * Routes to server API or direct library operation based on execution context.\n *\n * @param options - List command options\n * @param context - Execution context (server or local)\n * @returns List result containing formatted items\n */\nexport async function executeList(\n options: ListCommandOptions,\n context: ExecutionContext\n): Promise<ListCommandResult> {\n validateOptions(options);\n const format = getListFormat(options);\n\n if (context.type === \"server\") {\n // Use server's list API with format option\n return context.client.list({ format });\n }\n\n // Direct library operation\n return listReferences(context.library, { format });\n}\n\n/**\n * Format list result for CLI output.\n *\n * @param result - List result\n * @returns Formatted output string\n */\nexport function formatListOutput(result: ListCommandResult): string {\n if (result.items.length === 0) {\n return \"\";\n }\n return result.items.join(\"\\n\");\n}\n","import { unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport type { FulltextType } from \"../../features/fulltext/index.js\";\nimport { type RemoveResult, removeReference } from \"../../features/operations/remove.js\";\nimport type { ExecutionContext } from \"../execution-context.js\";\n\n/**\n * Options for the remove command.\n */\nexport interface RemoveCommandOptions {\n identifier: string;\n byUuid?: boolean;\n}\n\n/**\n * Result from remove command execution.\n */\nexport type RemoveCommandResult = RemoveResult;\n\n/**\n * Execute remove command.\n * Routes to server API or direct library operation based on execution context.\n *\n * @param options - Remove command options\n * @param context - Execution context (server or local)\n * @returns Remove result\n */\nexport async function executeRemove(\n options: RemoveCommandOptions,\n context: ExecutionContext\n): Promise<RemoveCommandResult> {\n const { identifier, byUuid = false } = options;\n\n if (context.type === \"server\") {\n // Server mode: use client with byUuid option for direct ID or UUID lookup\n return context.client.remove(identifier, { byUuid });\n }\n\n // Local mode: direct library operation\n return removeReference(context.library, { identifier, byUuid });\n}\n\n/**\n * Format remove result for CLI output.\n *\n * @param result - Remove result\n * @param identifier - The identifier that was used\n * @returns Formatted output string\n */\nexport function formatRemoveOutput(result: RemoveCommandResult, identifier: string): string {\n if (!result.removed) {\n return `Reference not found: ${identifier}`;\n }\n\n const item = result.item;\n if (item) {\n return `Removed: [${item.id}] ${item.title || \"(no title)\"}`;\n }\n\n return `Removed reference: ${identifier}`;\n}\n\n/**\n * Get fulltext attachment types from a CSL item.\n *\n * @param item - CSL item to check\n * @returns Array of attached fulltext types\n */\nexport function getFulltextAttachmentTypes(item: CslItem): FulltextType[] {\n const types: FulltextType[] = [];\n const fulltext = item.custom?.fulltext;\n\n if (fulltext?.pdf) {\n types.push(\"pdf\");\n }\n if (fulltext?.markdown) {\n types.push(\"markdown\");\n }\n\n return types;\n}\n\n/**\n * Format fulltext warning message for remove confirmation.\n *\n * @param types - Attached fulltext types\n * @returns Warning message string\n */\nexport function formatFulltextWarning(types: FulltextType[]): string {\n const typeLabels = types.map((t) => (t === \"pdf\" ? \"PDF\" : \"Markdown\"));\n const fileTypes = typeLabels.join(\" and \");\n return `Warning: This reference has fulltext files attached (${fileTypes}). Use --force to also delete the fulltext files.`;\n}\n\n/**\n * Delete fulltext files associated with a CSL item.\n *\n * @param item - CSL item with fulltext attachments\n * @param fulltextDirectory - Directory containing fulltext files\n */\nexport async function deleteFulltextFiles(item: CslItem, fulltextDirectory: string): Promise<void> {\n const fulltext = item.custom?.fulltext;\n if (!fulltext) {\n return;\n }\n\n const filesToDelete: string[] = [];\n\n if (fulltext.pdf) {\n filesToDelete.push(join(fulltextDirectory, fulltext.pdf));\n }\n if (fulltext.markdown) {\n filesToDelete.push(join(fulltextDirectory, fulltext.markdown));\n }\n\n for (const filePath of filesToDelete) {\n try {\n await unlink(filePath);\n } catch {\n // Ignore errors (file might not exist)\n }\n }\n}\n","import {\n type SearchFormat,\n type SearchResult,\n searchReferences,\n} from \"../../features/operations/search.js\";\nimport type { ExecutionContext } from \"../execution-context.js\";\n\n/**\n * Options for the search command.\n */\nexport interface SearchCommandOptions {\n query: string;\n json?: boolean;\n idsOnly?: boolean;\n uuid?: boolean;\n bibtex?: boolean;\n}\n\n/**\n * Result from search command execution.\n */\nexport type SearchCommandResult = SearchResult;\n\n/**\n * Convert CLI options to SearchFormat.\n */\nfunction getSearchFormat(options: SearchCommandOptions): SearchFormat {\n if (options.json) return \"json\";\n if (options.idsOnly) return \"ids-only\";\n if (options.uuid) return \"uuid\";\n if (options.bibtex) return \"bibtex\";\n return \"pretty\";\n}\n\n/**\n * Validate that only one output format is specified.\n */\nfunction validateOptions(options: SearchCommandOptions): void {\n const outputOptions = [options.json, options.idsOnly, options.uuid, options.bibtex].filter(\n Boolean\n );\n\n if (outputOptions.length > 1) {\n throw new Error(\n \"Multiple output formats specified. Only one of --json, --ids-only, --uuid, --bibtex can be used.\"\n );\n }\n}\n\n/**\n * Execute search command.\n * Routes to server API or direct library operation based on execution context.\n *\n * @param options - Search command options\n * @param context - Execution context (server or local)\n * @returns Search result containing formatted items\n */\nexport async function executeSearch(\n options: SearchCommandOptions,\n context: ExecutionContext\n): Promise<SearchCommandResult> {\n validateOptions(options);\n const format = getSearchFormat(options);\n\n if (context.type === \"server\") {\n // Use server's search API with format option\n return context.client.search({ query: options.query, format });\n }\n\n // Direct library operation\n return searchReferences(context.library, { query: options.query, format });\n}\n\n/**\n * Format search result for CLI output.\n *\n * @param result - Search result\n * @returns Formatted output string\n */\nexport function formatSearchOutput(result: SearchCommandResult): string {\n if (result.items.length === 0) {\n return \"\";\n }\n return result.items.join(\"\\n\");\n}\n","import {\n isProcessRunning,\n readPortfile,\n removePortfile,\n writePortfile,\n} from \"../../server/portfile.js\";\n\nexport interface ServerStartOptions {\n port?: number;\n daemon?: boolean;\n library: string;\n portfilePath: string;\n}\n\nexport interface ServerInfo {\n port: number;\n pid: number;\n library: string;\n started_at?: string;\n}\n\n/**\n * Start HTTP server.\n *\n * @param options - Server start options\n */\nexport async function serverStart(options: ServerStartOptions): Promise<void> {\n // Check if server is already running\n const existingStatus = await serverStatus(options.portfilePath);\n if (existingStatus !== null) {\n throw new Error(\"Server is already running\");\n }\n\n // Determine port (use provided port or default to 3000 for testing)\n const port = options.port ?? 3000;\n\n // For daemon mode, create portfile with current process PID\n // (In real implementation, this would be the spawned server process PID)\n const pid = process.pid;\n const started_at = new Date().toISOString();\n\n await writePortfile(options.portfilePath, port, pid, options.library, started_at);\n\n // In real implementation, this would spawn the server process\n // For now, we just create the portfile for testing purposes\n}\n\n/**\n * Stop running server.\n *\n * @param portfilePath - Path to the portfile\n */\nexport async function serverStop(portfilePath: string): Promise<void> {\n // Check if server is running\n const status = await serverStatus(portfilePath);\n if (status === null) {\n throw new Error(\"Server is not running\");\n }\n\n // In real implementation, send SIGTERM to the server process\n // For testing, we just remove the portfile\n await removePortfile(portfilePath);\n\n process.stdout.write(\"Server stopped successfully\\n\");\n}\n\n/**\n * Get server status.\n *\n * @param portfilePath - Path to the portfile\n * @returns Server info if running, null otherwise\n */\nexport async function serverStatus(portfilePath: string): Promise<ServerInfo | null> {\n // Read portfile\n const portfileData = await readPortfile(portfilePath);\n if (portfileData === null) {\n return null;\n }\n\n // Check if process is still running\n if (!isProcessRunning(portfileData.pid)) {\n // Process not found, cleanup stale portfile\n await removePortfile(portfilePath);\n return null;\n }\n\n // Return server info\n const result: ServerInfo = {\n port: portfileData.port,\n pid: portfileData.pid,\n library: portfileData.library ?? \"\",\n };\n\n if (portfileData.started_at !== undefined) {\n result.started_at = portfileData.started_at;\n }\n\n return result;\n}\n","import type { CslItem } from \"../../core/csl-json/types.js\";\nimport { type UpdateOperationResult, updateReference } from \"../../features/operations/update.js\";\nimport type { ExecutionContext } from \"../execution-context.js\";\n\n/**\n * Options for the update command.\n */\nexport interface UpdateCommandOptions {\n identifier: string;\n updates: Partial<CslItem>;\n byUuid?: boolean;\n}\n\n/**\n * Result from update command execution.\n */\nexport type UpdateCommandResult = UpdateOperationResult;\n\n/**\n * Execute update command.\n * Routes to server API or direct library operation based on execution context.\n *\n * @param options - Update command options\n * @param context - Execution context (server or local)\n * @returns Update result\n */\nexport async function executeUpdate(\n options: UpdateCommandOptions,\n context: ExecutionContext\n): Promise<UpdateCommandResult> {\n const { identifier, updates, byUuid = false } = options;\n\n if (context.type === \"server\") {\n // Server mode: use client with byUuid option for direct ID or UUID lookup\n return context.client.update(identifier, updates, { byUuid });\n }\n\n // Local mode: direct library operation\n return updateReference(context.library, { identifier, updates, byUuid });\n}\n\n/**\n * Format update result for CLI output.\n *\n * @param result - Update result\n * @param identifier - The identifier that was used\n * @returns Formatted output string\n */\nexport function formatUpdateOutput(result: UpdateCommandResult, identifier: string): string {\n if (!result.updated) {\n if (result.idCollision) {\n return `Update failed: ID collision for ${identifier}`;\n }\n return `Reference not found: ${identifier}`;\n }\n\n const item = result.item;\n const parts: string[] = [];\n\n if (item) {\n parts.push(`Updated: [${item.id}] ${item.title || \"(no title)\"}`);\n } else {\n parts.push(`Updated reference: ${identifier}`);\n }\n\n if (result.idChanged && result.newId) {\n parts.push(`ID changed to: ${result.newId}`);\n }\n\n return parts.join(\"\\n\");\n}\n","import type { CslItem } from \"../core/csl-json/types.js\";\nimport type { AddReferencesResult } from \"../features/operations/add.js\";\nimport type { CiteResult } from \"../features/operations/cite.js\";\nimport type { ListOptions, ListResult } from \"../features/operations/list.js\";\nimport type { RemoveResult } from \"../features/operations/remove.js\";\nimport type { SearchOperationOptions, SearchResult } from \"../features/operations/search.js\";\nimport type { UpdateOperationResult } from \"../features/operations/update.js\";\n\n/**\n * Options for addFromInputs method.\n */\nexport interface AddFromInputsOptions {\n force?: boolean;\n format?: string;\n}\n\n/**\n * Options for cite method.\n */\nexport interface CiteOptions {\n identifiers: string[];\n byUuid?: boolean;\n inText?: boolean;\n style?: string;\n cslFile?: string;\n locale?: string;\n format?: \"text\" | \"html\";\n}\n\n/**\n * Client for communicating with the reference-manager HTTP server.\n */\nexport class ServerClient {\n constructor(private baseUrl: string) {}\n\n /**\n * Get all references from the server.\n * @returns Array of CSL items\n */\n async getAll(): Promise<CslItem[]> {\n const url = `${this.baseUrl}/api/references`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(await response.text());\n }\n\n return (await response.json()) as CslItem[];\n }\n\n /**\n * Find reference by identifier.\n * @param identifier - Citation ID or UUID\n * @param options - Options object\n * @param options.byUuid - If true, treat identifier as UUID; otherwise as citation ID\n * @returns CSL item or null if not found\n */\n async find(identifier: string, options?: { byUuid?: boolean }): Promise<CslItem | null> {\n const path = options?.byUuid\n ? `/api/references/uuid/${identifier}`\n : `/api/references/id/${identifier}`;\n const url = `${this.baseUrl}${path}`;\n const response = await fetch(url);\n\n if (response.status === 404) {\n return null;\n }\n\n if (!response.ok) {\n throw new Error(await response.text());\n }\n\n return (await response.json()) as CslItem;\n }\n\n /**\n * Add new reference to the library.\n * @param item - CSL item to add\n * @returns Created CSL item with UUID\n */\n async add(item: CslItem): Promise<CslItem> {\n const url = `${this.baseUrl}/api/references`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(item),\n });\n\n if (!response.ok) {\n throw new Error(await response.text());\n }\n\n return (await response.json()) as CslItem;\n }\n\n /**\n * Update existing reference.\n * @param identifier - Citation ID or UUID\n * @param updates - Partial CSL item with fields to update\n * @param options - Options object\n * @param options.byUuid - If true, treat identifier as UUID; otherwise as citation ID\n * @returns Update operation result\n */\n async update(\n identifier: string,\n updates: Partial<CslItem>,\n options?: { byUuid?: boolean }\n ): Promise<UpdateOperationResult> {\n const path = options?.byUuid\n ? `/api/references/uuid/${identifier}`\n : `/api/references/id/${identifier}`;\n const url = `${this.baseUrl}${path}`;\n const response = await fetch(url, {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(updates),\n });\n\n if (!response.ok && response.status !== 404 && response.status !== 409) {\n throw new Error(await response.text());\n }\n\n return (await response.json()) as UpdateOperationResult;\n }\n\n /**\n * Remove reference by identifier.\n * @param identifier - Citation ID or UUID\n * @param options - Options object\n * @param options.byUuid - If true, treat identifier as UUID; otherwise as citation ID\n * @returns Remove operation result\n */\n async remove(identifier: string, options?: { byUuid?: boolean }): Promise<RemoveResult> {\n const path = options?.byUuid\n ? `/api/references/uuid/${identifier}`\n : `/api/references/id/${identifier}`;\n const url = `${this.baseUrl}${path}`;\n const response = await fetch(url, {\n method: \"DELETE\",\n });\n\n if (!response.ok && response.status !== 404) {\n throw new Error(await response.text());\n }\n\n return (await response.json()) as RemoveResult;\n }\n\n /**\n * Add references from various input formats.\n * @param inputs - Array of inputs (file paths, PMIDs, DOIs)\n * @param options - Options for add operation\n * @returns Result containing added, failed, and skipped items\n */\n async addFromInputs(\n inputs: string[],\n options?: AddFromInputsOptions\n ): Promise<AddReferencesResult> {\n const url = `${this.baseUrl}/api/add`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ inputs, options }),\n });\n\n if (!response.ok) {\n throw new Error(await response.text());\n }\n\n return (await response.json()) as AddReferencesResult;\n }\n\n /**\n * Generate citations for references.\n * @param options - Cite options including identifiers and formatting\n * @returns Cite result with per-identifier results\n */\n async cite(options: CiteOptions): Promise<CiteResult> {\n const url = `${this.baseUrl}/api/cite`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(options),\n });\n\n if (!response.ok) {\n throw new Error(await response.text());\n }\n\n return (await response.json()) as CiteResult;\n }\n\n /**\n * List all references with optional formatting.\n * @param options - List options including format\n * @returns List result with formatted items\n */\n async list(options?: ListOptions): Promise<ListResult> {\n const url = `${this.baseUrl}/api/list`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(options ?? {}),\n });\n\n if (!response.ok) {\n throw new Error(await response.text());\n }\n\n return (await response.json()) as ListResult;\n }\n\n /**\n * Search references with query.\n * @param options - Search options including query and format\n * @returns Search result with formatted items\n */\n async search(options: SearchOperationOptions): Promise<SearchResult> {\n const url = `${this.baseUrl}/api/search`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(options),\n });\n\n if (!response.ok) {\n throw new Error(await response.text());\n }\n\n return (await response.json()) as SearchResult;\n }\n}\n","import { spawn } from \"node:child_process\";\nimport type { Config } from \"../config/schema.js\";\nimport {\n getPortfilePath,\n isProcessRunning,\n portfileExists,\n readPortfile,\n removePortfile,\n} from \"../server/portfile.js\";\n\n/**\n * Server connection information.\n */\nexport interface ServerConnection {\n baseUrl: string;\n pid: number;\n}\n\n/**\n * Get server connection if available.\n * @param libraryPath - Path to the library file\n * @param config - Configuration\n * @returns Server connection or null if not available\n */\nexport async function getServerConnection(\n libraryPath: string,\n config: Config\n): Promise<ServerConnection | null> {\n const portfilePath = getPortfilePath();\n const portfileData = await readPortfile(portfilePath);\n\n // Check if portfile exists\n if (!portfileData) {\n // No server running\n if (config.server.autoStart) {\n // Auto-start server\n await startServerDaemon(libraryPath, config);\n await waitForPortfile(5000); // 5 second timeout\n // Retry connection\n return await getServerConnection(libraryPath, config);\n }\n return null;\n }\n\n // Check if process is running\n if (!isProcessRunning(portfileData.pid)) {\n // Stale portfile, remove it\n await removePortfile(portfilePath);\n return null;\n }\n\n // Check if library path matches\n if (!portfileData.library || portfileData.library !== libraryPath) {\n // Server is serving a different library\n return null;\n }\n\n // Server is running and serving our library\n return {\n baseUrl: `http://localhost:${portfileData.port}`,\n pid: portfileData.pid,\n };\n}\n\n/**\n * Start server in daemon mode.\n * @param libraryPath - Path to the library file\n * @param _config - Configuration (reserved for future use)\n */\nexport async function startServerDaemon(libraryPath: string, _config: Config): Promise<void> {\n // Get the binary path (argv[1] is the script being executed)\n const binaryPath = process.argv[1] || process.execPath;\n\n // Spawn server in detached daemon mode\n const child = spawn(\n process.execPath,\n [binaryPath, \"server\", \"start\", \"--daemon\", \"--library\", libraryPath],\n {\n detached: true,\n stdio: \"ignore\",\n }\n );\n\n child.unref(); // Allow parent to exit\n}\n\n/**\n * Wait for portfile to appear.\n * @param timeoutMs - Timeout in milliseconds\n */\nexport async function waitForPortfile(timeoutMs: number): Promise<void> {\n const portfilePath = getPortfilePath();\n const startTime = Date.now();\n const checkInterval = 50; // Check every 50ms\n\n while (Date.now() - startTime < timeoutMs) {\n if (await portfileExists(portfilePath)) {\n return;\n }\n // Wait before next check\n await new Promise((resolve) => setTimeout(resolve, checkInterval));\n }\n\n throw new Error(`Server failed to start: portfile not created within ${timeoutMs}ms`);\n}\n","/**\n * Execution Context for CLI Commands\n *\n * Provides a discriminated union type to distinguish between server and local execution modes.\n * This enables CLI commands to avoid redundant library loading when a server is available.\n */\n\nimport type { Config } from \"../config/schema.js\";\nimport type { Library } from \"../core/library.js\";\nimport { ServerClient } from \"./server-client.js\";\nimport { getServerConnection } from \"./server-detection.js\";\n\n/**\n * Server execution context - uses ServerClient for operations\n */\nexport interface ServerExecutionContext {\n type: \"server\";\n client: ServerClient;\n}\n\n/**\n * Local execution context - uses Library directly\n */\nexport interface LocalExecutionContext {\n type: \"local\";\n library: Library;\n}\n\n/**\n * Discriminated union type for execution context.\n * Commands can use this to determine whether to use server or local operations.\n *\n * @example\n * ```typescript\n * if (context.type === \"server\") {\n * // Use context.client for server operations\n * const result = await context.client.list(options);\n * } else {\n * // Use context.library for local operations\n * const items = context.library.getAll();\n * }\n * ```\n */\nexport type ExecutionContext = ServerExecutionContext | LocalExecutionContext;\n\n/**\n * Creates an execution context based on the current configuration.\n *\n * This function checks for an available server connection first.\n * If a server is available, it returns a server context to avoid loading the library.\n * Otherwise, it loads the library and returns a local context.\n *\n * @param config - The application configuration\n * @param loadLibrary - Function to load the library (injected for testability)\n * @returns Promise resolving to either a server or local execution context\n */\nexport async function createExecutionContext(\n config: Config,\n loadLibrary: (path: string) => Promise<Library>\n): Promise<ExecutionContext> {\n // Check for server connection first\n const server = await getServerConnection(config.library, config);\n\n if (server) {\n // Server is available - use server context to avoid loading library\n return {\n type: \"server\",\n client: new ServerClient(server.baseUrl),\n };\n }\n\n // No server available - load library for local operations\n const library = await loadLibrary(config.library);\n return {\n type: \"local\",\n library,\n };\n}\n\n/**\n * Type guard to check if context is a server context\n */\nexport function isServerContext(context: ExecutionContext): context is ServerExecutionContext {\n return context.type === \"server\";\n}\n\n/**\n * Type guard to check if context is a local context\n */\nexport function isLocalContext(context: ExecutionContext): context is LocalExecutionContext {\n return context.type === \"local\";\n}\n","/**\n * CLI Helper Functions\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { stdin, stdout } from \"node:process\";\nimport { loadConfig } from \"../config/loader.js\";\nimport type { Config } from \"../config/schema.js\";\n\n/**\n * Output format type\n */\nexport type OutputFormat = \"pretty\" | \"json\" | \"ids-only\" | \"uuid\" | \"bibtex\";\n\n/**\n * CLI options interface\n */\nexport interface CliOptions {\n library?: string;\n logLevel?: \"silent\" | \"info\" | \"debug\";\n config?: string;\n quiet?: boolean;\n verbose?: boolean;\n backup?: boolean;\n backupDir?: string;\n json?: boolean;\n idsOnly?: boolean;\n uuid?: boolean;\n bibtex?: boolean;\n}\n\n/**\n * Read JSON input from file or stdin\n * @param file - File path (optional, defaults to stdin)\n * @returns JSON string\n */\nexport async function readJsonInput(file?: string): Promise<string> {\n if (file) {\n // Read from file\n try {\n return readFileSync(file, \"utf-8\");\n } catch (error) {\n throw new Error(\n `I/O error: Cannot read file ${file}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n // Read from stdin\n const chunks: Buffer[] = [];\n for await (const chunk of stdin) {\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks).toString(\"utf-8\");\n}\n\n/**\n * Parse JSON input\n * @param input - JSON string\n * @returns Parsed JSON object or array\n */\nexport function parseJsonInput(input: string): unknown {\n if (!input || input.trim() === \"\") {\n throw new Error(\"Parse error: Empty input\");\n }\n\n try {\n return JSON.parse(input);\n } catch (error) {\n throw new Error(\n `Parse error: Invalid JSON: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * Load config with CLI argument overrides\n * @param options - CLI options\n * @returns Config with overrides applied\n */\nexport async function loadConfigWithOverrides(options: CliOptions): Promise<Config> {\n // Load base config\n // Note: options.config is currently ignored as loadConfig uses cwd and userConfigPath\n const config = await loadConfig();\n\n // Apply CLI overrides\n const overrides: Partial<Config> = {};\n\n // Library path\n if (options.library) {\n overrides.library = options.library;\n }\n\n // Log level\n if (options.quiet) {\n overrides.logLevel = \"silent\";\n } else if (options.verbose) {\n overrides.logLevel = \"debug\";\n } else if (options.logLevel) {\n overrides.logLevel = options.logLevel;\n }\n\n // Backup settings\n if (options.backup !== undefined || options.backupDir) {\n overrides.backup = {\n ...config.backup,\n ...(options.backup !== undefined && { enabled: options.backup }),\n ...(options.backupDir && { directory: options.backupDir }),\n };\n }\n\n return { ...config, ...overrides };\n}\n\n/**\n * Get output format from options\n * @param options - CLI options\n * @returns Output format\n * @throws Error if multiple formats specified\n */\nexport function getOutputFormat(options: CliOptions): OutputFormat {\n const formats: OutputFormat[] = [];\n\n if (options.json) formats.push(\"json\");\n if (options.idsOnly) formats.push(\"ids-only\");\n if (options.uuid) formats.push(\"uuid\");\n if (options.bibtex) formats.push(\"bibtex\");\n\n if (formats.length > 1) {\n throw new Error(\n \"Multiple output formats specified. Only one of --json, --ids-only, --uuid, --bibtex can be used.\"\n );\n }\n\n return formats[0] || \"pretty\";\n}\n\n/**\n * Check if running in TTY\n * @returns True if stdin and stdout are TTY\n */\nexport function isTTY(): boolean {\n return Boolean(stdin.isTTY && stdout.isTTY);\n}\n\n/**\n * Read confirmation from user (y/N)\n * @param prompt - Confirmation prompt message\n * @returns True if user confirmed (y/yes), false otherwise\n */\nexport async function readConfirmation(prompt: string): Promise<boolean> {\n // If not TTY, auto-confirm\n if (!isTTY()) {\n return true;\n }\n\n // Display prompt\n stdout.write(`${prompt} (y/N): `);\n\n // Read input\n const chunks: Buffer[] = [];\n for await (const chunk of stdin) {\n chunks.push(chunk as Buffer);\n // Break after first line\n const input = Buffer.concat(chunks).toString(\"utf-8\");\n if (input.includes(\"\\n\")) {\n break;\n }\n }\n\n const input = Buffer.concat(chunks).toString(\"utf-8\").trim().toLowerCase();\n return input === \"y\" || input === \"yes\";\n}\n\n/**\n * Read stdin content and split into inputs.\n * Used for reading identifiers from stdin.\n * @returns Array of input strings (split by whitespace)\n */\nexport async function readStdinInputs(): Promise<string[]> {\n const content = await readStdinContent();\n\n if (!content) {\n return [];\n }\n\n // Split by whitespace (space, tab, newline)\n return content.split(/\\s+/).filter((s) => s.length > 0);\n}\n\n/**\n * Read raw stdin content without splitting.\n * Used for reading file content (JSON, BibTeX, RIS) from stdin.\n * @returns Raw stdin content as string\n */\nexport async function readStdinContent(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of stdin) {\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks).toString(\"utf-8\").trim();\n}\n\n/**\n * Read raw stdin content as Buffer.\n * Used for reading binary file content (PDF) from stdin.\n * @returns Raw stdin content as Buffer\n */\nexport async function readStdinBuffer(): Promise<Buffer> {\n const chunks: Buffer[] = [];\n for await (const chunk of stdin) {\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks);\n}\n","/**\n * CLI Entry Point\n */\n\nimport { Command } from \"commander\";\nimport { z } from \"zod\";\n// Import package.json for version and description\nimport packageJson from \"../../package.json\" with { type: \"json\" };\nimport type { CslItem } from \"../core/csl-json/types.js\";\nimport { Library } from \"../core/library.js\";\nimport { getPortfilePath } from \"../server/portfile.js\";\nimport { executeAdd, formatAddOutput, getExitCode } from \"./commands/add.js\";\nimport {\n type CiteCommandOptions,\n executeCite,\n formatCiteErrors,\n formatCiteOutput,\n getCiteExitCode,\n} from \"./commands/cite.js\";\nimport {\n type FulltextAttachOptions,\n type FulltextDetachOptions,\n type FulltextGetOptions,\n executeFulltextAttach,\n executeFulltextDetach,\n executeFulltextGet,\n formatFulltextAttachOutput,\n formatFulltextDetachOutput,\n formatFulltextGetOutput,\n getFulltextExitCode,\n} from \"./commands/fulltext.js\";\nimport { type ListCommandOptions, executeList, formatListOutput } from \"./commands/list.js\";\nimport {\n type RemoveCommandOptions,\n deleteFulltextFiles,\n executeRemove,\n formatFulltextWarning,\n formatRemoveOutput,\n getFulltextAttachmentTypes,\n} from \"./commands/remove.js\";\nimport { type SearchCommandOptions, executeSearch, formatSearchOutput } from \"./commands/search.js\";\nimport { serverStart, serverStatus, serverStop } from \"./commands/server.js\";\nimport { type UpdateCommandOptions, executeUpdate, formatUpdateOutput } from \"./commands/update.js\";\nimport { type ExecutionContext, createExecutionContext } from \"./execution-context.js\";\nimport type { CliOptions } from \"./helpers.js\";\nimport {\n isTTY,\n loadConfigWithOverrides,\n parseJsonInput,\n readConfirmation,\n readJsonInput,\n readStdinBuffer,\n readStdinContent,\n} from \"./helpers.js\";\n\n/**\n * Create Commander program instance\n */\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name(\"reference-manager\")\n .version(packageJson.version)\n .description(packageJson.description);\n\n // Global options\n program\n .option(\"--library <path>\", \"Override library file path\")\n .option(\"--log-level <level>\", \"Override log level (silent|info|debug)\")\n .option(\"--config <path>\", \"Use specific config file\")\n .option(\"--quiet\", \"Suppress all non-error output\")\n .option(\"--verbose\", \"Enable verbose output\")\n .option(\"--no-backup\", \"Disable backup creation\")\n .option(\"--backup-dir <path>\", \"Override backup directory\");\n\n // Register commands\n registerListCommand(program);\n registerSearchCommand(program);\n registerAddCommand(program);\n registerRemoveCommand(program);\n registerUpdateCommand(program);\n registerCiteCommand(program);\n registerServerCommand(program);\n registerFulltextCommand(program);\n\n return program;\n}\n\n/**\n * Handle 'list' command action\n */\nasync function handleListAction(options: ListCommandOptions, program: Command): Promise<void> {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n\n const context = await createExecutionContext(config, Library.load);\n const result = await executeList(options, context);\n const output = formatListOutput(result);\n\n if (output) {\n process.stdout.write(`${output}\\n`);\n }\n\n process.exit(0);\n } catch (error) {\n process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(4);\n }\n}\n\n/**\n * Register 'list' command\n */\nfunction registerListCommand(program: Command): void {\n program\n .command(\"list\")\n .description(\"List all references in the library\")\n .option(\"--json\", \"Output in JSON format\")\n .option(\"--ids-only\", \"Output only citation keys\")\n .option(\"--uuid\", \"Output only UUIDs\")\n .option(\"--bibtex\", \"Output in BibTeX format\")\n .action(async (options) => {\n await handleListAction(options, program);\n });\n}\n\n/**\n * Handle 'search' command action\n */\nasync function handleSearchAction(\n query: string,\n options: Omit<SearchCommandOptions, \"query\">,\n program: Command\n): Promise<void> {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n\n const context = await createExecutionContext(config, Library.load);\n const result = await executeSearch({ ...options, query }, context);\n const output = formatSearchOutput(result);\n\n if (output) {\n process.stdout.write(`${output}\\n`);\n }\n\n process.exit(0);\n } catch (error) {\n process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(4);\n }\n}\n\n/**\n * Register 'search' command\n */\nfunction registerSearchCommand(program: Command): void {\n program\n .command(\"search\")\n .description(\"Search references\")\n .argument(\"<query>\", \"Search query\")\n .option(\"--json\", \"Output in JSON format\")\n .option(\"--ids-only\", \"Output only citation keys\")\n .option(\"--uuid\", \"Output only UUIDs\")\n .option(\"--bibtex\", \"Output in BibTeX format\")\n .action(async (query: string, options) => {\n await handleSearchAction(query, options, program);\n });\n}\n\n/**\n * Register 'add' command\n */\ninterface AddCommandOptions extends CliOptions {\n force?: boolean;\n format?: string;\n verbose?: boolean;\n}\n\nasync function handleAddAction(\n inputs: string[],\n options: AddCommandOptions,\n program: Command\n): Promise<void> {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n\n // If no inputs provided, read content from stdin\n let stdinContent: string | undefined;\n if (inputs.length === 0) {\n stdinContent = await readStdinContent();\n }\n\n // Create execution context\n const context = await createExecutionContext(config, Library.load);\n\n // Build add options - avoid undefined values for exactOptionalPropertyTypes\n const addOptions: Parameters<typeof executeAdd>[0] = {\n inputs,\n force: options.force ?? false,\n };\n if (options.format !== undefined) {\n addOptions.format = options.format;\n }\n if (options.verbose !== undefined) {\n addOptions.verbose = options.verbose;\n }\n if (stdinContent?.trim()) {\n addOptions.stdinContent = stdinContent;\n }\n // Build pubmedConfig only if values exist\n const pubmedConfig: { email?: string; apiKey?: string } = {};\n if (config.pubmed.email !== undefined) {\n pubmedConfig.email = config.pubmed.email;\n }\n if (config.pubmed.apiKey !== undefined) {\n pubmedConfig.apiKey = config.pubmed.apiKey;\n }\n if (Object.keys(pubmedConfig).length > 0) {\n addOptions.pubmedConfig = pubmedConfig;\n }\n\n // Execute add command\n const result = await executeAdd(addOptions, context);\n\n // Format and output result\n const output = formatAddOutput(result, options.verbose ?? false);\n process.stderr.write(`${output}\\n`);\n\n // Exit with appropriate code\n process.exit(getExitCode(result));\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(1);\n }\n}\n\nfunction registerAddCommand(program: Command): void {\n program\n .command(\"add\")\n .description(\"Add new reference(s) to the library\")\n .argument(\"[input...]\", \"File paths or identifiers (PMID/DOI), or use stdin\")\n .option(\"-f, --force\", \"Skip duplicate detection\")\n .option(\"--format <format>\", \"Explicit input format: json|bibtex|ris|pmid|doi|auto\", \"auto\")\n .option(\"--verbose\", \"Show detailed error information\")\n .action(async (inputs: string[], options: AddCommandOptions) => {\n await handleAddAction(inputs, options, program);\n });\n}\n\n/**\n * Register 'remove' command\n */\nasync function findReferenceToRemove(\n identifier: string,\n byUuid: boolean,\n context: ExecutionContext\n): Promise<CslItem | undefined> {\n if (context.type === \"server\") {\n const item = await context.client.find(identifier, { byUuid });\n return item ?? undefined;\n }\n\n const ref = byUuid\n ? context.library.findByUuid(identifier)\n : context.library.findById(identifier);\n return ref?.getItem();\n}\n\nasync function confirmRemoval(\n refToRemove: CslItem,\n force: boolean,\n fulltextWarning?: string\n): Promise<boolean> {\n if (force || !isTTY()) {\n return true;\n }\n\n const authors = Array.isArray(refToRemove.author)\n ? refToRemove.author.map((a) => `${a.family || \"\"}, ${a.given?.[0] || \"\"}.`).join(\"; \")\n : \"(no authors)\";\n let confirmMsg = `Remove reference [${refToRemove.id}]?\\nTitle: ${refToRemove.title || \"(no title)\"}\\nAuthors: ${authors}`;\n\n if (fulltextWarning) {\n confirmMsg += `\\n\\n${fulltextWarning}`;\n }\n\n confirmMsg += \"\\nContinue?\";\n\n return await readConfirmation(confirmMsg);\n}\n\nfunction handleRemoveError(error: unknown): never {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"not found\")) {\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(1);\n }\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(4);\n}\n\n/**\n * Format type labels for fulltext files.\n */\nfunction formatTypeLabels(types: (\"pdf\" | \"markdown\")[]): string {\n return types.map((t) => (t === \"pdf\" ? \"PDF\" : \"Markdown\")).join(\" and \");\n}\n\n/**\n * Handle fulltext deletion and output.\n */\nasync function handleFulltextDeletion(\n item: CslItem,\n fulltextDirectory: string,\n types: (\"pdf\" | \"markdown\")[],\n output: string\n): Promise<void> {\n await deleteFulltextFiles(item, fulltextDirectory);\n const typeLabels = formatTypeLabels(types);\n process.stderr.write(`${output}\\n`);\n process.stderr.write(`Deleted fulltext files: ${typeLabels}\\n`);\n}\n\nasync function handleRemoveAction(\n identifier: string,\n options: { uuid?: boolean; force?: boolean },\n program: Command\n): Promise<void> {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n const context = await createExecutionContext(config, Library.load);\n\n // Find reference for confirmation display\n const refToRemove = await findReferenceToRemove(identifier, options.uuid ?? false, context);\n\n if (!refToRemove) {\n process.stderr.write(`Error: Reference not found: ${identifier}\\n`);\n process.exit(1);\n }\n\n // Check for fulltext attachments\n const fulltextTypes = getFulltextAttachmentTypes(refToRemove);\n const hasFulltext = fulltextTypes.length > 0;\n\n // If fulltext attached and not TTY without --force, require --force\n if (hasFulltext && !isTTY() && !options.force) {\n const warning = formatFulltextWarning(fulltextTypes);\n process.stderr.write(`Error: ${warning}\\n`);\n process.exit(1);\n }\n\n // Interactive confirmation (with fulltext warning if applicable)\n const fulltextWarning =\n hasFulltext && !options.force ? formatFulltextWarning(fulltextTypes) : undefined;\n const confirmed = await confirmRemoval(refToRemove, options.force ?? false, fulltextWarning);\n if (!confirmed) {\n process.stderr.write(\"Cancelled.\\n\");\n process.exit(2);\n }\n\n // Execute removal using unified pattern\n const removeOptions: RemoveCommandOptions = {\n identifier,\n };\n if (options.uuid !== undefined) {\n removeOptions.byUuid = options.uuid;\n }\n\n const result = await executeRemove(removeOptions, context);\n const output = formatRemoveOutput(result, identifier);\n\n if (!result.removed) {\n process.stderr.write(`${output}\\n`);\n process.exit(1);\n }\n\n // Delete fulltext files if --force and fulltext was attached\n if (hasFulltext && options.force) {\n await handleFulltextDeletion(refToRemove, config.fulltext.directory, fulltextTypes, output);\n } else {\n process.stderr.write(`${output}\\n`);\n }\n process.exit(0);\n } catch (error) {\n handleRemoveError(error);\n }\n}\n\nfunction registerRemoveCommand(program: Command): void {\n program\n .command(\"remove\")\n .description(\"Remove a reference from the library\")\n .argument(\"<identifier>\", \"Citation key or UUID\")\n .option(\"--uuid\", \"Interpret identifier as UUID\")\n .option(\"-f, --force\", \"Skip confirmation prompt\")\n .action(async (identifier: string, options) => {\n await handleRemoveAction(identifier, options, program);\n });\n}\n\nfunction handleUpdateError(error: unknown): never {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"Parse error\")) {\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(3);\n }\n if (message.includes(\"not found\") || message.includes(\"validation\")) {\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(1);\n }\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(4);\n}\n\nasync function handleUpdateAction(\n identifier: string,\n file: string | undefined,\n options: { uuid?: boolean },\n program: Command\n): Promise<void> {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n\n const inputStr = await readJsonInput(file);\n const updates = parseJsonInput(inputStr);\n\n // Validate that updates is a non-null object using zod\n const updatesSchema = z.record(z.string(), z.unknown());\n const validatedUpdates = updatesSchema.parse(updates);\n\n const context = await createExecutionContext(config, Library.load);\n\n const updateOptions: UpdateCommandOptions = {\n identifier,\n updates: validatedUpdates as Partial<CslItem>,\n };\n if (options.uuid !== undefined) {\n updateOptions.byUuid = options.uuid;\n }\n\n const result = await executeUpdate(updateOptions, context);\n const output = formatUpdateOutput(result, identifier);\n\n if (result.updated) {\n process.stderr.write(`${output}\\n`);\n process.exit(0);\n } else {\n process.stderr.write(`${output}\\n`);\n process.exit(1);\n }\n } catch (error) {\n handleUpdateError(error);\n }\n}\n\nfunction registerUpdateCommand(program: Command): void {\n program\n .command(\"update\")\n .description(\"Update fields of an existing reference\")\n .argument(\"<identifier>\", \"Citation key or UUID\")\n .argument(\"[file]\", \"JSON file with updates (or use stdin)\")\n .option(\"--uuid\", \"Interpret identifier as UUID\")\n .action(async (identifier: string, file: string | undefined, options) => {\n await handleUpdateAction(identifier, file, options, program);\n });\n}\n\n/**\n * Handle 'cite' command action\n */\nasync function handleCiteAction(\n identifiers: string[],\n options: Omit<CiteCommandOptions, \"identifiers\">,\n program: Command\n): Promise<void> {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n\n const context = await createExecutionContext(config, Library.load);\n const result = await executeCite({ ...options, identifiers }, context);\n\n // Output successful citations\n const output = formatCiteOutput(result);\n if (output) {\n process.stdout.write(`${output}\\n`);\n }\n\n // Output errors\n const errors = formatCiteErrors(result);\n if (errors) {\n process.stderr.write(`${errors}\\n`);\n }\n\n process.exit(getCiteExitCode(result));\n } catch (error) {\n process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(4);\n }\n}\n\n/**\n * Register 'cite' command\n */\nfunction registerCiteCommand(program: Command): void {\n program\n .command(\"cite\")\n .description(\"Generate formatted citations for references\")\n .argument(\"<id-or-uuid...>\", \"Citation keys or UUIDs to cite\")\n .option(\"--uuid\", \"Treat arguments as UUIDs instead of IDs\")\n .option(\"--style <style>\", \"CSL style name\")\n .option(\"--csl-file <path>\", \"Path to custom CSL file\")\n .option(\"--locale <locale>\", \"Locale code (e.g., en-US, ja-JP)\")\n .option(\"--format <format>\", \"Output format: text|html|rtf\")\n .option(\"--in-text\", \"Generate in-text citations instead of bibliography entries\")\n .action(async (identifiers: string[], options) => {\n await handleCiteAction(identifiers, options, program);\n });\n}\n\n/**\n * Register 'server' command\n */\nfunction registerServerCommand(program: Command): void {\n const serverCmd = program.command(\"server\").description(\"Manage HTTP server for library access\");\n\n serverCmd\n .command(\"start\")\n .description(\"Start HTTP server\")\n .option(\"--port <port>\", \"Specify port number\")\n .option(\"-d, --daemon\", \"Run in background\")\n .action(async (options) => {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n\n const portfilePath = getPortfilePath();\n\n const startOptions = {\n library: config.library,\n portfilePath,\n daemon: options.daemon,\n ...(options.port && { port: Number.parseInt(options.port, 10) }),\n };\n\n await serverStart(startOptions);\n\n process.exit(0);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"already running\") || message.includes(\"conflict\")) {\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(1);\n }\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(4);\n }\n });\n\n serverCmd\n .command(\"stop\")\n .description(\"Stop running server\")\n .action(async () => {\n try {\n const portfilePath = getPortfilePath();\n await serverStop(portfilePath);\n\n process.stderr.write(\"Server stopped.\\n\");\n process.exit(0);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"not running\")) {\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(1);\n }\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(4);\n }\n });\n\n serverCmd\n .command(\"status\")\n .description(\"Check server status\")\n .action(async () => {\n try {\n const portfilePath = getPortfilePath();\n const status = await serverStatus(portfilePath);\n\n if (status) {\n process.stdout.write(\n `Server is running\\nPort: ${status.port}\\nPID: ${status.pid}\\nLibrary: ${status.library}\\n`\n );\n process.exit(0);\n } else {\n process.stdout.write(\"Server not running\\n\");\n process.exit(1);\n }\n } catch (error) {\n process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(4);\n }\n });\n}\n\n/**\n * Check if an option value is a valid file path (not a boolean flag)\n */\nfunction isValidFilePath(value: string | boolean | undefined): value is string {\n return typeof value === \"string\" && value !== \"\" && value !== \"true\";\n}\n\n/**\n * Parse fulltext attach options to determine file type and path\n */\nfunction parseFulltextAttachTypeAndPath(\n filePathArg: string | undefined,\n options: { pdf?: string | boolean; markdown?: string | boolean }\n): { type: \"pdf\" | \"markdown\" | undefined; filePath: string | undefined } {\n if (options.pdf) {\n return { type: \"pdf\", filePath: isValidFilePath(options.pdf) ? options.pdf : filePathArg };\n }\n if (options.markdown) {\n return {\n type: \"markdown\",\n filePath: isValidFilePath(options.markdown) ? options.markdown : filePathArg,\n };\n }\n return { type: undefined, filePath: filePathArg };\n}\n\n/**\n * Handle 'fulltext attach' command action\n */\nasync function handleFulltextAttachAction(\n identifier: string,\n filePathArg: string | undefined,\n options: {\n pdf?: string;\n markdown?: string;\n move?: boolean;\n force?: boolean;\n uuid?: boolean;\n },\n program: Command\n): Promise<void> {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n const context = await createExecutionContext(config, Library.load);\n\n const { type, filePath } = parseFulltextAttachTypeAndPath(filePathArg, options);\n\n // If no file path and type is specified, read from stdin\n const stdinContent = !filePath && type ? await readStdinBuffer() : undefined;\n\n const attachOptions: FulltextAttachOptions = {\n identifier,\n fulltextDirectory: config.fulltext.directory,\n ...(filePath && { filePath }),\n ...(type && { type }),\n ...(options.move && { move: options.move }),\n ...(options.force && { force: options.force }),\n ...(options.uuid && { byUuid: options.uuid }),\n ...(stdinContent && { stdinContent }),\n };\n\n const result = await executeFulltextAttach(attachOptions, context);\n const output = formatFulltextAttachOutput(result);\n process.stderr.write(`${output}\\n`);\n process.exit(getFulltextExitCode(result));\n } catch (error) {\n process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(4);\n }\n}\n\n/**\n * Handle 'fulltext get' command action\n */\nasync function handleFulltextGetAction(\n identifier: string,\n options: {\n pdf?: boolean;\n markdown?: boolean;\n stdout?: boolean;\n uuid?: boolean;\n },\n program: Command\n): Promise<void> {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n\n const context = await createExecutionContext(config, Library.load);\n\n const getOptions: FulltextGetOptions = {\n identifier,\n fulltextDirectory: config.fulltext.directory,\n ...(options.pdf && { type: \"pdf\" as const }),\n ...(options.markdown && { type: \"markdown\" as const }),\n ...(options.stdout && { stdout: options.stdout }),\n ...(options.uuid && { byUuid: options.uuid }),\n };\n\n const result = await executeFulltextGet(getOptions, context);\n\n if (result.success && result.content && options.stdout) {\n // Write raw content to stdout\n process.stdout.write(result.content);\n } else {\n const output = formatFulltextGetOutput(result);\n if (result.success) {\n process.stdout.write(`${output}\\n`);\n } else {\n process.stderr.write(`${output}\\n`);\n }\n }\n\n process.exit(getFulltextExitCode(result));\n } catch (error) {\n process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(4);\n }\n}\n\n/**\n * Handle 'fulltext detach' command action\n */\nasync function handleFulltextDetachAction(\n identifier: string,\n options: {\n pdf?: boolean;\n markdown?: boolean;\n delete?: boolean;\n force?: boolean;\n uuid?: boolean;\n },\n program: Command\n): Promise<void> {\n try {\n const globalOpts = program.opts();\n const config = await loadConfigWithOverrides({ ...globalOpts, ...options });\n\n const context = await createExecutionContext(config, Library.load);\n\n const detachOptions: FulltextDetachOptions = {\n identifier,\n fulltextDirectory: config.fulltext.directory,\n ...(options.pdf && { type: \"pdf\" as const }),\n ...(options.markdown && { type: \"markdown\" as const }),\n ...(options.delete && { delete: options.delete }),\n ...(options.force && { force: options.force }),\n ...(options.uuid && { byUuid: options.uuid }),\n };\n\n const result = await executeFulltextDetach(detachOptions, context);\n const output = formatFulltextDetachOutput(result);\n process.stderr.write(`${output}\\n`);\n\n process.exit(getFulltextExitCode(result));\n } catch (error) {\n process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(4);\n }\n}\n\n/**\n * Register 'fulltext' command with subcommands\n */\nfunction registerFulltextCommand(program: Command): void {\n const fulltextCmd = program\n .command(\"fulltext\")\n .description(\"Manage full-text files attached to references\");\n\n fulltextCmd\n .command(\"attach\")\n .description(\"Attach a full-text file to a reference\")\n .argument(\"<identifier>\", \"Citation key or UUID\")\n .argument(\"[file-path]\", \"Path to the file to attach\")\n .option(\"--pdf [path]\", \"Attach as PDF (path optional if provided as argument)\")\n .option(\"--markdown [path]\", \"Attach as Markdown (path optional if provided as argument)\")\n .option(\"--move\", \"Move file instead of copy\")\n .option(\"-f, --force\", \"Overwrite existing attachment\")\n .option(\"--uuid\", \"Interpret identifier as UUID\")\n .action(async (identifier: string, filePath: string | undefined, options) => {\n await handleFulltextAttachAction(identifier, filePath, options, program);\n });\n\n fulltextCmd\n .command(\"get\")\n .description(\"Get full-text file path or content\")\n .argument(\"<identifier>\", \"Citation key or UUID\")\n .option(\"--pdf\", \"Get PDF file only\")\n .option(\"--markdown\", \"Get Markdown file only\")\n .option(\"--stdout\", \"Output file content to stdout\")\n .option(\"--uuid\", \"Interpret identifier as UUID\")\n .action(async (identifier: string, options) => {\n await handleFulltextGetAction(identifier, options, program);\n });\n\n fulltextCmd\n .command(\"detach\")\n .description(\"Detach full-text file from a reference\")\n .argument(\"<identifier>\", \"Citation key or UUID\")\n .option(\"--pdf\", \"Detach PDF only\")\n .option(\"--markdown\", \"Detach Markdown only\")\n .option(\"--delete\", \"Also delete the file from disk\")\n .option(\"-f, --force\", \"Skip confirmation for delete\")\n .option(\"--uuid\", \"Interpret identifier as UUID\")\n .action(async (identifier: string, options) => {\n await handleFulltextDetachAction(identifier, options, program);\n });\n}\n\n/**\n * Main CLI entry point\n */\nexport async function main(argv: string[]): Promise<void> {\n const program = createProgram();\n\n // Setup signal handlers\n process.on(\"SIGINT\", () => {\n process.exit(130);\n });\n\n process.on(\"SIGTERM\", () => {\n process.exit(0);\n });\n\n await program.parseAsync(argv);\n}\n"],"names":["fs","validateOptions","stdout","path","input"],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * CSL Style Management
3
+ *
4
+ * Handles resolution and loading of CSL (Citation Style Language) style files.
5
+ *
6
+ * Style Resolution Order:
7
+ * 1. --csl-file <path> (exact file path)
8
+ * 2. Built-in style matching --style <name>
9
+ * 3. Search in csl_directory paths (in array order)
10
+ * 4. Default style from config (default_style)
11
+ * 5. "apa" (hardcoded default)
12
+ */
13
+ /**
14
+ * Built-in styles available in @citation-js/plugin-csl
15
+ * These can be used directly without loading external files
16
+ */
17
+ export declare const BUILTIN_STYLES: readonly ["apa", "vancouver", "harvard"];
18
+ export type BuiltinStyleName = (typeof BUILTIN_STYLES)[number];
19
+ /**
20
+ * Check if a style name is a built-in style
21
+ */
22
+ export declare function isBuiltinStyle(styleName: string): styleName is BuiltinStyleName;
23
+ export interface StyleResolutionOptions {
24
+ /**
25
+ * Exact path to CSL file (from --csl-file option)
26
+ * Takes highest priority
27
+ */
28
+ cslFile?: string;
29
+ /**
30
+ * Style name to resolve (from --style option)
31
+ */
32
+ style?: string;
33
+ /**
34
+ * Directory or directories to search for custom CSL files
35
+ * (from csl_directory config)
36
+ * Can be a single string or array of strings
37
+ */
38
+ cslDirectory?: string | string[];
39
+ /**
40
+ * Default style to use if specified style not found
41
+ * (from default_style config)
42
+ */
43
+ defaultStyle?: string;
44
+ }
45
+ export interface StyleResolution {
46
+ /**
47
+ * Type of resolution: "builtin" for citation-js built-in styles,
48
+ * "custom" for external CSL files
49
+ */
50
+ type: "builtin" | "custom";
51
+ /**
52
+ * The resolved style name (for built-in) or identifier (for custom)
53
+ */
54
+ styleName: string;
55
+ /**
56
+ * CSL XML content (only for custom styles)
57
+ */
58
+ styleXml?: string;
59
+ }
60
+ /**
61
+ * Load CSL style file content from the given path
62
+ *
63
+ * @param stylePath - Path to the CSL style file
64
+ * @returns Content of the CSL style file (XML string)
65
+ * @throws Error if file cannot be read
66
+ */
67
+ export declare function loadCSLStyleFile(stylePath: string): string;
68
+ /**
69
+ * Resolve the style based on resolution options
70
+ *
71
+ * Resolution order:
72
+ * 1. cslFile (exact path) - throws if doesn't exist
73
+ * 2. Built-in style matching style name
74
+ * 3. Search in csl_directory paths (in order)
75
+ * 4. Default style (defaultStyle) - if built-in
76
+ * 5. "apa" (hardcoded fallback)
77
+ *
78
+ * @param options - Style resolution options
79
+ * @returns StyleResolution with type, styleName, and optional styleXml
80
+ * @throws Error if cslFile is specified but doesn't exist
81
+ */
82
+ export declare function resolveStyle(options: StyleResolutionOptions): StyleResolution;
83
+ //# sourceMappingURL=csl-styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csl-styles.d.ts","sourceRoot":"","sources":["../../src/config/csl-styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH;;;GAGG;AACH,eAAO,MAAM,cAAc,0CAA2C,CAAC;AAEvE,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,IAAI,gBAAgB,CAE/E;AAED,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAEjC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;IAE3B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAaD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CA4D7E"}