@ncukondo/reference-manager 0.13.5 → 0.14.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.
- package/dist/chunks/{action-menu-svBJmZdi.js → action-menu-DNlpGiwS.js} +2 -2
- package/dist/chunks/{action-menu-svBJmZdi.js.map → action-menu-DNlpGiwS.js.map} +1 -1
- package/dist/chunks/{index-CaAOawzv.js → index-UpzsmbyY.js} +27081 -26872
- package/dist/chunks/index-UpzsmbyY.js.map +1 -0
- package/dist/chunks/reference-select-DSVwE9iu.js +52 -0
- package/dist/chunks/reference-select-DSVwE9iu.js.map +1 -0
- package/dist/chunks/{search-prompt-Bg9usf8f.js → search-prompt-BrWpOcij.js} +34 -1
- package/dist/chunks/{search-prompt-Bg9usf8f.js.map → search-prompt-BrWpOcij.js.map} +1 -1
- package/dist/chunks/style-select-CHjDTyq2.js +86 -0
- package/dist/chunks/style-select-CHjDTyq2.js.map +1 -0
- package/dist/cli/commands/cite.d.ts +5 -1
- package/dist/cli/commands/cite.d.ts.map +1 -1
- package/dist/cli/commands/edit.d.ts +13 -1
- package/dist/cli/commands/edit.d.ts.map +1 -1
- package/dist/cli/commands/fulltext.d.ts +54 -1
- package/dist/cli/commands/fulltext.d.ts.map +1 -1
- package/dist/cli/commands/remove.d.ts +14 -1
- package/dist/cli/commands/remove.d.ts.map +1 -1
- package/dist/cli/commands/update.d.ts +18 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/helpers.d.ts +11 -0
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli.js +1 -3
- package/dist/cli.js.map +1 -1
- package/dist/features/interactive/reference-select.d.ts +79 -0
- package/dist/features/interactive/reference-select.d.ts.map +1 -0
- package/dist/features/interactive/search-prompt.d.ts.map +1 -1
- package/dist/features/interactive/style-select.d.ts +56 -0
- package/dist/features/interactive/style-select.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/chunks/index-CaAOawzv.js.map +0 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { t as tokenize, s as search } from "./file-watcher-D2Y-SlcE.js";
|
|
2
|
+
import { runSearchPrompt } from "./search-prompt-BrWpOcij.js";
|
|
3
|
+
import { checkTTY } from "./tty-CDBIQraQ.js";
|
|
4
|
+
async function runReferenceSelect(allReferences, options, config) {
|
|
5
|
+
checkTTY();
|
|
6
|
+
const searchFn = (query) => {
|
|
7
|
+
const { tokens } = tokenize(query);
|
|
8
|
+
return search(allReferences, tokens);
|
|
9
|
+
};
|
|
10
|
+
const searchResult = await runSearchPrompt(
|
|
11
|
+
allReferences,
|
|
12
|
+
searchFn,
|
|
13
|
+
config,
|
|
14
|
+
options.initialQuery ?? ""
|
|
15
|
+
);
|
|
16
|
+
if (searchResult.cancelled) {
|
|
17
|
+
return { selected: [], cancelled: true };
|
|
18
|
+
}
|
|
19
|
+
const selected = options.multiSelect ? searchResult.selected : searchResult.selected.slice(0, 1);
|
|
20
|
+
return {
|
|
21
|
+
selected,
|
|
22
|
+
cancelled: false
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async function selectReferencesOrExit(allReferences, options, config) {
|
|
26
|
+
if (allReferences.length === 0) {
|
|
27
|
+
process.stderr.write("No references in library.\n");
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
const selectResult = await runReferenceSelect(allReferences, options, config);
|
|
31
|
+
if (selectResult.cancelled || selectResult.selected.length === 0) {
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
return selectResult.selected.map((item) => item.id);
|
|
35
|
+
}
|
|
36
|
+
async function selectReferenceItemsOrExit(allReferences, options, config) {
|
|
37
|
+
if (allReferences.length === 0) {
|
|
38
|
+
process.stderr.write("No references in library.\n");
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
const selectResult = await runReferenceSelect(allReferences, options, config);
|
|
42
|
+
if (selectResult.cancelled || selectResult.selected.length === 0) {
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
return selectResult.selected;
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
runReferenceSelect,
|
|
49
|
+
selectReferenceItemsOrExit,
|
|
50
|
+
selectReferencesOrExit
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=reference-select-DSVwE9iu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference-select-DSVwE9iu.js","sources":["../../src/features/interactive/reference-select.ts"],"sourcesContent":["/**\n * Shared reference selection utility for interactive ID selection.\n *\n * This module provides a reusable function to select references interactively\n * using the existing search prompt infrastructure.\n */\n\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport { search } from \"../search/matcher.js\";\nimport { tokenize } from \"../search/tokenizer.js\";\nimport { type SearchPromptConfig, runSearchPrompt } from \"./search-prompt.js\";\nimport { checkTTY } from \"./tty.js\";\n\n/**\n * Options for reference selection\n */\nexport interface ReferenceSelectOptions {\n /** Whether to allow multiple selection (default: true) */\n multiSelect: boolean;\n /** Custom prompt message */\n prompt?: string;\n /** Initial search query */\n initialQuery?: string;\n}\n\n/**\n * Result from reference selection\n */\nexport interface ReferenceSelectResult {\n /** Selected references */\n selected: CslItem[];\n /** Whether the selection was cancelled */\n cancelled: boolean;\n}\n\n/**\n * Run interactive reference selection.\n *\n * Launches an interactive search prompt to select references from the library.\n * Supports both single and multiple selection modes.\n *\n * @param allReferences - All references available for selection\n * @param options - Selection options\n * @param config - Interactive prompt configuration\n * @returns Selection result with selected references\n * @throws TTYError if not running in a TTY environment\n */\nexport async function runReferenceSelect(\n allReferences: CslItem[],\n options: ReferenceSelectOptions,\n config: SearchPromptConfig\n): Promise<ReferenceSelectResult> {\n // Check TTY requirement\n checkTTY();\n\n // Create search function for runSearchPrompt\n const searchFn = (query: string) => {\n const { tokens } = tokenize(query);\n return search(allReferences, tokens);\n };\n\n // Run search prompt\n const searchResult = await runSearchPrompt(\n allReferences,\n searchFn,\n config,\n options.initialQuery ?? \"\"\n );\n\n if (searchResult.cancelled) {\n return { selected: [], cancelled: true };\n }\n\n // For single-select mode, return only the first selected item\n const selected = options.multiSelect ? searchResult.selected : searchResult.selected.slice(0, 1);\n\n return {\n selected,\n cancelled: false,\n };\n}\n\n/**\n * Options for selectReferencesOrExit helper\n */\nexport interface SelectReferencesOrExitOptions {\n /** Whether to allow multiple selection */\n multiSelect: boolean;\n /** Initial search query */\n initialQuery?: string;\n}\n\n/**\n * Select references interactively or exit if cancelled/empty.\n *\n * This is a convenience wrapper around runReferenceSelect that handles\n * common patterns:\n * - Exits with code 0 if library is empty\n * - Exits with code 0 if selection is cancelled\n * - Returns selected identifiers\n *\n * @param allReferences - All references available for selection\n * @param options - Selection options\n * @param config - Interactive prompt configuration\n * @returns Array of selected reference IDs (never empty)\n */\nexport async function selectReferencesOrExit(\n allReferences: CslItem[],\n options: SelectReferencesOrExitOptions,\n config: SearchPromptConfig\n): Promise<string[]> {\n if (allReferences.length === 0) {\n process.stderr.write(\"No references in library.\\n\");\n process.exit(0);\n }\n\n const selectResult = await runReferenceSelect(allReferences, options, config);\n\n if (selectResult.cancelled || selectResult.selected.length === 0) {\n process.exit(0);\n }\n\n return selectResult.selected.map((item) => item.id);\n}\n\n/**\n * Select reference items interactively or exit if cancelled/empty.\n *\n * Similar to selectReferencesOrExit but returns full CslItem objects\n * instead of just identifiers. Useful when the caller needs access to\n * the full reference data (e.g., for confirmation dialogs).\n *\n * @param allReferences - All references available for selection\n * @param options - Selection options\n * @param config - Interactive prompt configuration\n * @returns Array of selected CslItem objects (never empty)\n */\nexport async function selectReferenceItemsOrExit(\n allReferences: CslItem[],\n options: SelectReferencesOrExitOptions,\n config: SearchPromptConfig\n): Promise<CslItem[]> {\n if (allReferences.length === 0) {\n process.stderr.write(\"No references in library.\\n\");\n process.exit(0);\n }\n\n const selectResult = await runReferenceSelect(allReferences, options, config);\n\n if (selectResult.cancelled || selectResult.selected.length === 0) {\n process.exit(0);\n }\n\n return selectResult.selected;\n}\n"],"names":[],"mappings":";;;AA+CA,eAAsB,mBACpB,eACA,SACA,QACgC;AAEhC,WAAA;AAGA,QAAM,WAAW,CAAC,UAAkB;AAClC,UAAM,EAAE,OAAA,IAAW,SAAS,KAAK;AACjC,WAAO,OAAO,eAAe,MAAM;AAAA,EACrC;AAGA,QAAM,eAAe,MAAM;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,gBAAgB;AAAA,EAAA;AAG1B,MAAI,aAAa,WAAW;AAC1B,WAAO,EAAE,UAAU,IAAI,WAAW,KAAA;AAAA,EACpC;AAGA,QAAM,WAAW,QAAQ,cAAc,aAAa,WAAW,aAAa,SAAS,MAAM,GAAG,CAAC;AAE/F,SAAO;AAAA,IACL;AAAA,IACA,WAAW;AAAA,EAAA;AAEf;AA0BA,eAAsB,uBACpB,eACA,SACA,QACmB;AACnB,MAAI,cAAc,WAAW,GAAG;AAC9B,YAAQ,OAAO,MAAM,6BAA6B;AAClD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,eAAe,MAAM,mBAAmB,eAAe,SAAS,MAAM;AAE5E,MAAI,aAAa,aAAa,aAAa,SAAS,WAAW,GAAG;AAChE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAO,aAAa,SAAS,IAAI,CAAC,SAAS,KAAK,EAAE;AACpD;AAcA,eAAsB,2BACpB,eACA,SACA,QACoB;AACpB,MAAI,cAAc,WAAW,GAAG;AAC9B,YAAQ,OAAO,MAAM,6BAA6B;AAClD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,eAAe,MAAM,mBAAmB,eAAe,SAAS,MAAM;AAE5E,MAAI,aAAa,aAAa,aAAa,SAAS,WAAW,GAAG;AAChE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAO,aAAa;AACtB;"}
|
|
@@ -195,11 +195,44 @@ async function runSearchPrompt(allReferences, searchFn, config, initialQuery = "
|
|
|
195
195
|
promptInstance.space = function() {
|
|
196
196
|
return this.append(" ");
|
|
197
197
|
};
|
|
198
|
+
const promptWithDelete = promptInstance;
|
|
199
|
+
if (typeof promptWithDelete.delete === "function") {
|
|
200
|
+
const originalDelete = promptWithDelete.delete.bind(promptWithDelete);
|
|
201
|
+
promptWithDelete.delete = function() {
|
|
202
|
+
if (this.cursor <= 0) {
|
|
203
|
+
return this.alert();
|
|
204
|
+
}
|
|
205
|
+
return originalDelete();
|
|
206
|
+
};
|
|
207
|
+
}
|
|
198
208
|
const promptWithRender = promptInstance;
|
|
199
209
|
promptWithRender.next = function() {
|
|
200
210
|
promptInstance.toggle(promptInstance.focused);
|
|
201
211
|
this.render();
|
|
202
212
|
};
|
|
213
|
+
const promptWithRestore = promptInstance;
|
|
214
|
+
if (typeof promptWithRestore.restore === "function") {
|
|
215
|
+
const originalRestore = promptWithRestore.restore.bind(promptWithRestore);
|
|
216
|
+
promptWithRestore.restore = function() {
|
|
217
|
+
if (this.options.show === false) return;
|
|
218
|
+
const sections = this.sections();
|
|
219
|
+
const size = sections.rest.length;
|
|
220
|
+
this.state.size = size;
|
|
221
|
+
if (size > 0) {
|
|
222
|
+
const basePrompt = this.state.prompt || "";
|
|
223
|
+
const cursorPos = this.cursor;
|
|
224
|
+
const stripAnsi = (str) => str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
|
|
225
|
+
const promptDisplayWidth = stripAnsi(basePrompt).length;
|
|
226
|
+
const targetCol = promptDisplayWidth + cursorPos;
|
|
227
|
+
let codes = "";
|
|
228
|
+
codes += `\x1B[${size}A`;
|
|
229
|
+
codes += `\x1B[${targetCol + 1}G`;
|
|
230
|
+
this.stdout.write(codes);
|
|
231
|
+
} else {
|
|
232
|
+
originalRestore();
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
203
236
|
const result = await promptInstance.run();
|
|
204
237
|
const promptAny = prompt;
|
|
205
238
|
let valuesToParse = result;
|
|
@@ -229,4 +262,4 @@ export {
|
|
|
229
262
|
parseSelectedValues,
|
|
230
263
|
runSearchPrompt
|
|
231
264
|
};
|
|
232
|
-
//# sourceMappingURL=search-prompt-
|
|
265
|
+
//# sourceMappingURL=search-prompt-BrWpOcij.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-prompt-Bg9usf8f.js","sources":["../../src/features/interactive/format.ts","../../src/features/interactive/search-prompt.ts"],"sourcesContent":["/**\n * Display format functions for interactive search\n */\n\nimport type { CslItem } from \"../../core/csl-json/types.js\";\n\n/**\n * CSL name type (author structure)\n */\ntype CslName = NonNullable<CslItem[\"author\"]>[number];\n\n/**\n * Format a single author name\n * - Personal: \"Smith, J.\" (family + initial of given)\n * - Institutional: \"World Health Organization\" (literal)\n */\nfunction formatSingleAuthor(author: CslName): string {\n if (author.literal) {\n return author.literal;\n }\n if (author.family) {\n if (author.given) {\n const initial = author.given.charAt(0).toUpperCase();\n return `${author.family}, ${initial}.`;\n }\n return author.family;\n }\n return \"\";\n}\n\n/**\n * Format author list for display\n * - Single author: \"Smith, J.\"\n * - Two authors: \"Smith, J., & Doe, A.\"\n * - Three authors: \"Smith, J., Doe, A., & Johnson, B.\"\n * - More than three: \"Smith, J., et al.\"\n *\n * @param authors - Array of CSL author objects\n * @returns Formatted author string\n */\nexport function formatAuthors(authors: CslName[] | undefined): string {\n if (!authors || authors.length === 0) {\n return \"\";\n }\n\n if (authors.length > 3) {\n const first = authors[0];\n if (!first) {\n return \"\";\n }\n return `${formatSingleAuthor(first)}, et al.`;\n }\n\n const formatted = authors.map(formatSingleAuthor);\n if (formatted.length === 1) {\n return formatted[0] ?? \"\";\n }\n\n // Join all but last with \", \" and append \"& \" + last\n const allButLast = formatted.slice(0, -1).join(\", \");\n const last = formatted[formatted.length - 1] ?? \"\";\n return `${allButLast}, & ${last}`;\n}\n\n/**\n * Truncate title to fit terminal width\n *\n * @param title - Title string\n * @param maxWidth - Maximum display width\n * @returns Truncated title with ellipsis if needed\n */\nexport function formatTitle(title: string | undefined, maxWidth: number): string {\n if (!title) {\n return \"\";\n }\n\n if (title.length <= maxWidth) {\n return title;\n }\n\n // Truncate and add ellipsis, keeping total length at maxWidth\n return `${title.slice(0, maxWidth - 3)}...`;\n}\n\n/**\n * Format identifiers (DOI, PMID, PMCID, ISBN) for display\n *\n * @param item - CSL item\n * @returns Formatted identifier string (e.g., \"DOI: 10.1000/example | PMID: 12345678\")\n */\nexport function formatIdentifiers(item: CslItem): string {\n const identifiers: string[] = [];\n\n if (item.DOI) {\n identifiers.push(`DOI: ${item.DOI}`);\n }\n if (item.PMID) {\n identifiers.push(`PMID: ${item.PMID}`);\n }\n if (item.PMCID) {\n identifiers.push(`PMCID: ${item.PMCID}`);\n }\n if (item.ISBN) {\n identifiers.push(`ISBN: ${item.ISBN}`);\n }\n\n return identifiers.join(\" | \");\n}\n\n/**\n * Extract year from CSL item\n */\nfunction extractYear(item: CslItem): number | undefined {\n const dateParts = item.issued?.[\"date-parts\"];\n if (!dateParts || dateParts.length === 0) {\n return undefined;\n }\n const firstDatePart = dateParts[0];\n if (!firstDatePart || firstDatePart.length === 0) {\n return undefined;\n }\n return firstDatePart[0];\n}\n\n/**\n * Compose a complete search result line\n *\n * Format:\n * ```\n * [1] Smith, J., & Doe, A. (2020)\n * Machine learning in medicine: A comprehensive review\n * DOI: 10.1000/example | PMID: 12345678\n * ```\n *\n * @param item - CSL item\n * @param index - Display index (1-based)\n * @param terminalWidth - Terminal width for title truncation\n * @returns Multi-line formatted string\n */\nexport function formatSearchResult(item: CslItem, index: number, terminalWidth: number): string {\n const lines: string[] = [];\n\n // Line 1: [index] Authors (year)\n const authors = formatAuthors(item.author);\n const year = extractYear(item);\n const yearPart = year !== undefined ? ` (${year})` : \"\";\n const line1 = `[${index}] ${authors}${yearPart}`;\n lines.push(line1);\n\n // Line 2: Title (indented, truncated)\n const indent = \" \";\n const titleMaxWidth = terminalWidth - indent.length;\n const title = formatTitle(item.title, titleMaxWidth);\n if (title) {\n lines.push(`${indent}${title}`);\n }\n\n // Line 3: Identifiers (indented)\n const identifiers = formatIdentifiers(item);\n if (identifiers) {\n lines.push(`${indent}${identifiers}`);\n }\n\n return lines.join(\"\\n\");\n}\n","/**\n * Interactive search prompt using Enquirer's AutoComplete\n *\n * Provides real-time incremental search with multiple selection support.\n */\n\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport type { SearchResult } from \"../search/types.js\";\nimport type { AutoCompleteChoice } from \"./enquirer.js\";\nimport { formatSearchResult } from \"./format.js\";\n\n/**\n * Configuration for the search prompt\n */\nexport interface SearchPromptConfig {\n /** Maximum number of results to display */\n limit: number;\n /** Debounce delay in milliseconds */\n debounceMs: number;\n}\n\n/**\n * Search function type for filtering references\n */\nexport type SearchFunction = (query: string) => SearchResult[];\n\n/**\n * Result from the search prompt\n */\nexport interface SearchPromptResult {\n /** Selected references */\n selected: CslItem[];\n /** Whether the prompt was cancelled */\n cancelled: boolean;\n}\n\n/**\n * Maps internal choice value to CslItem\n */\ninterface ChoiceData {\n index: number;\n item: CslItem;\n}\n\n/**\n * Creates choices from search results\n */\nexport function createChoices(\n results: SearchResult[],\n terminalWidth: number\n): AutoCompleteChoice[] {\n return results.map((result, index) => {\n const displayIndex = index + 1;\n const formattedText = formatSearchResult(result.reference, displayIndex, terminalWidth);\n\n // Enquirer returns the 'name' property on selection when 'value' is not defined\n // So we store the JSON data in 'name' and use 'message' for display\n return {\n name: JSON.stringify({\n index,\n item: result.reference,\n } satisfies ChoiceData),\n message: formattedText,\n };\n });\n}\n\n/**\n * Parses selected values back to CslItems\n */\nexport function parseSelectedValues(values: string | string[]): CslItem[] {\n const valueArray = Array.isArray(values) ? values : [values];\n const items: CslItem[] = [];\n\n for (const value of valueArray) {\n if (!value) continue;\n try {\n const data = JSON.parse(value) as ChoiceData;\n items.push(data.item);\n } catch {\n // If parsing fails, the value might be just the id (name)\n // In this case, we can't recover the full item\n // This shouldn't happen in normal operation\n }\n }\n\n return items;\n}\n\n/**\n * Gets terminal width, falling back to 80 if not available\n */\nexport function getTerminalWidth(): number {\n return process.stdout.columns ?? 80;\n}\n\n/**\n * Gets terminal height, falling back to 24 if not available\n */\nexport function getTerminalHeight(): number {\n return process.stdout.rows ?? 24;\n}\n\n/**\n * Calculates the effective limit for the autocomplete list\n * based on terminal height to prevent input field from being hidden.\n * Reserves space for: prompt header (1), input line (1), footer hint (1), and padding (2)\n * Each item displays up to 3 lines (author/year, title, identifiers)\n */\nexport function calculateEffectiveLimit(configLimit: number): number {\n const terminalHeight = getTerminalHeight();\n const reservedLines = 5; // header + input + footer hint + padding\n const linesPerItem = 3; // each search result shows up to 3 lines\n const availableLines = terminalHeight - reservedLines;\n const maxVisibleChoices = Math.max(1, Math.floor(availableLines / linesPerItem));\n return configLimit > 0 ? Math.min(configLimit, maxVisibleChoices) : maxVisibleChoices;\n}\n\n/**\n * Collects enabled (selected) item IDs from choices\n */\nfunction collectEnabledIds(choices: AutoCompleteChoice[]): Set<string> {\n const enabledIds = new Set<string>();\n for (const choice of choices) {\n if ((choice as { enabled?: boolean }).enabled) {\n try {\n const data = JSON.parse(choice.name) as ChoiceData;\n enabledIds.add(data.item.id);\n } catch {\n // Ignore parse errors\n }\n }\n }\n return enabledIds;\n}\n\n/**\n * Restores enabled state for choices matching the given IDs\n */\nfunction restoreEnabledState(choices: AutoCompleteChoice[], enabledIds: Set<string>): void {\n for (const choice of choices) {\n try {\n const data = JSON.parse(choice.name) as ChoiceData;\n if (enabledIds.has(data.item.id)) {\n (choice as { enabled?: boolean }).enabled = true;\n }\n } catch {\n // Ignore parse errors\n }\n }\n}\n\n/**\n * Creates and runs an interactive search prompt\n */\nexport async function runSearchPrompt(\n allReferences: CslItem[],\n searchFn: SearchFunction,\n config: SearchPromptConfig,\n initialQuery = \"\"\n): Promise<SearchPromptResult> {\n // Dynamic import to allow mocking in tests\n // enquirer is a CommonJS module, so we must use default import\n const enquirer = await import(\"enquirer\");\n const BaseAutoComplete = (enquirer.default as unknown as Record<string, unknown>)\n .AutoComplete as new (\n options: Record<string, unknown>\n ) => {\n run(): Promise<string | string[]>;\n };\n\n const terminalWidth = getTerminalWidth();\n // Calculate effective limit based on terminal height\n const effectiveLimit = calculateEffectiveLimit(config.limit);\n\n // Create initial choices from all references (limited)\n const initialResults: SearchResult[] = initialQuery\n ? searchFn(initialQuery).slice(0, effectiveLimit)\n : allReferences.slice(0, effectiveLimit).map((ref) => ({\n reference: ref,\n overallStrength: \"exact\" as const,\n tokenMatches: [],\n score: 0,\n }));\n\n const initialChoices = createChoices(initialResults, terminalWidth);\n\n // Track last search query to avoid redundant searches\n let lastQuery = initialQuery;\n\n const promptOptions = {\n name: \"references\",\n message: \"Search references\",\n initial: initialQuery,\n choices: initialChoices,\n multiple: true,\n limit: effectiveLimit,\n footer: \"(Tab: select, Enter: confirm)\",\n suggest: (input: string, choices: AutoCompleteChoice[]) => {\n // If input hasn't changed, return current choices\n if (input === lastQuery) {\n return choices;\n }\n lastQuery = input;\n\n // Collect enabled (selected) item IDs from current choices\n const enabledIds = collectEnabledIds(choices);\n\n // Create new choices based on input\n const newChoices = input.trim()\n ? createChoices(searchFn(input).slice(0, effectiveLimit), terminalWidth)\n : createChoices(\n allReferences.slice(0, effectiveLimit).map((ref) => ({\n reference: ref,\n overallStrength: \"exact\" as const,\n tokenMatches: [],\n score: 0,\n })),\n terminalWidth\n );\n\n // Restore enabled state for matching items\n restoreEnabledState(newChoices, enabledIds);\n\n return newChoices;\n },\n };\n\n try {\n const prompt = new BaseAutoComplete(promptOptions);\n\n // Override key handlers:\n // - Space: append character instead of toggling selection\n // - Tab: toggle selection (like space does in default multiple mode)\n const promptInstance = prompt as unknown as {\n space: () => void;\n dispatch: (s: string, key: { name: string }) => Promise<void>;\n append: (ch: string) => void;\n toggle: (item: unknown) => void;\n focused: unknown;\n run(): Promise<string | string[]>;\n };\n\n promptInstance.space = function () {\n return this.append(\" \");\n };\n\n // Override next() which is called on Tab key press\n // Make it toggle selection only (no movement, use arrow keys to move)\n const promptWithRender = promptInstance as unknown as {\n next: () => void;\n render: () => void;\n };\n promptWithRender.next = function () {\n promptInstance.toggle(promptInstance.focused);\n this.render();\n };\n\n const result = await promptInstance.run();\n\n // Workaround for Enquirer bug: focused item with enabled=true may not be in result\n const promptAny = prompt as unknown as {\n focused?: { name: string; enabled?: boolean };\n selected?: Array<{ name: string }>;\n };\n\n let valuesToParse: string | string[] = result;\n\n // If result is empty but focused item is enabled, use focused item\n if (\n Array.isArray(result) &&\n result.length === 0 &&\n promptAny.focused?.enabled &&\n promptAny.focused?.name\n ) {\n valuesToParse = [promptAny.focused.name];\n }\n\n // Handle result\n const selected = parseSelectedValues(valuesToParse);\n\n return {\n selected,\n cancelled: false,\n };\n } catch (error) {\n // Enquirer throws an empty string when cancelled\n if (error === \"\" || (error instanceof Error && error.message === \"\")) {\n return {\n selected: [],\n cancelled: true,\n };\n }\n throw error;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"search-prompt-BrWpOcij.js","sources":["../../src/features/interactive/format.ts","../../src/features/interactive/search-prompt.ts"],"sourcesContent":["/**\n * Display format functions for interactive search\n */\n\nimport type { CslItem } from \"../../core/csl-json/types.js\";\n\n/**\n * CSL name type (author structure)\n */\ntype CslName = NonNullable<CslItem[\"author\"]>[number];\n\n/**\n * Format a single author name\n * - Personal: \"Smith, J.\" (family + initial of given)\n * - Institutional: \"World Health Organization\" (literal)\n */\nfunction formatSingleAuthor(author: CslName): string {\n if (author.literal) {\n return author.literal;\n }\n if (author.family) {\n if (author.given) {\n const initial = author.given.charAt(0).toUpperCase();\n return `${author.family}, ${initial}.`;\n }\n return author.family;\n }\n return \"\";\n}\n\n/**\n * Format author list for display\n * - Single author: \"Smith, J.\"\n * - Two authors: \"Smith, J., & Doe, A.\"\n * - Three authors: \"Smith, J., Doe, A., & Johnson, B.\"\n * - More than three: \"Smith, J., et al.\"\n *\n * @param authors - Array of CSL author objects\n * @returns Formatted author string\n */\nexport function formatAuthors(authors: CslName[] | undefined): string {\n if (!authors || authors.length === 0) {\n return \"\";\n }\n\n if (authors.length > 3) {\n const first = authors[0];\n if (!first) {\n return \"\";\n }\n return `${formatSingleAuthor(first)}, et al.`;\n }\n\n const formatted = authors.map(formatSingleAuthor);\n if (formatted.length === 1) {\n return formatted[0] ?? \"\";\n }\n\n // Join all but last with \", \" and append \"& \" + last\n const allButLast = formatted.slice(0, -1).join(\", \");\n const last = formatted[formatted.length - 1] ?? \"\";\n return `${allButLast}, & ${last}`;\n}\n\n/**\n * Truncate title to fit terminal width\n *\n * @param title - Title string\n * @param maxWidth - Maximum display width\n * @returns Truncated title with ellipsis if needed\n */\nexport function formatTitle(title: string | undefined, maxWidth: number): string {\n if (!title) {\n return \"\";\n }\n\n if (title.length <= maxWidth) {\n return title;\n }\n\n // Truncate and add ellipsis, keeping total length at maxWidth\n return `${title.slice(0, maxWidth - 3)}...`;\n}\n\n/**\n * Format identifiers (DOI, PMID, PMCID, ISBN) for display\n *\n * @param item - CSL item\n * @returns Formatted identifier string (e.g., \"DOI: 10.1000/example | PMID: 12345678\")\n */\nexport function formatIdentifiers(item: CslItem): string {\n const identifiers: string[] = [];\n\n if (item.DOI) {\n identifiers.push(`DOI: ${item.DOI}`);\n }\n if (item.PMID) {\n identifiers.push(`PMID: ${item.PMID}`);\n }\n if (item.PMCID) {\n identifiers.push(`PMCID: ${item.PMCID}`);\n }\n if (item.ISBN) {\n identifiers.push(`ISBN: ${item.ISBN}`);\n }\n\n return identifiers.join(\" | \");\n}\n\n/**\n * Extract year from CSL item\n */\nfunction extractYear(item: CslItem): number | undefined {\n const dateParts = item.issued?.[\"date-parts\"];\n if (!dateParts || dateParts.length === 0) {\n return undefined;\n }\n const firstDatePart = dateParts[0];\n if (!firstDatePart || firstDatePart.length === 0) {\n return undefined;\n }\n return firstDatePart[0];\n}\n\n/**\n * Compose a complete search result line\n *\n * Format:\n * ```\n * [1] Smith, J., & Doe, A. (2020)\n * Machine learning in medicine: A comprehensive review\n * DOI: 10.1000/example | PMID: 12345678\n * ```\n *\n * @param item - CSL item\n * @param index - Display index (1-based)\n * @param terminalWidth - Terminal width for title truncation\n * @returns Multi-line formatted string\n */\nexport function formatSearchResult(item: CslItem, index: number, terminalWidth: number): string {\n const lines: string[] = [];\n\n // Line 1: [index] Authors (year)\n const authors = formatAuthors(item.author);\n const year = extractYear(item);\n const yearPart = year !== undefined ? ` (${year})` : \"\";\n const line1 = `[${index}] ${authors}${yearPart}`;\n lines.push(line1);\n\n // Line 2: Title (indented, truncated)\n const indent = \" \";\n const titleMaxWidth = terminalWidth - indent.length;\n const title = formatTitle(item.title, titleMaxWidth);\n if (title) {\n lines.push(`${indent}${title}`);\n }\n\n // Line 3: Identifiers (indented)\n const identifiers = formatIdentifiers(item);\n if (identifiers) {\n lines.push(`${indent}${identifiers}`);\n }\n\n return lines.join(\"\\n\");\n}\n","/**\n * Interactive search prompt using Enquirer's AutoComplete\n *\n * Provides real-time incremental search with multiple selection support.\n */\n\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport type { SearchResult } from \"../search/types.js\";\nimport type { AutoCompleteChoice } from \"./enquirer.js\";\nimport { formatSearchResult } from \"./format.js\";\n\n/**\n * Configuration for the search prompt\n */\nexport interface SearchPromptConfig {\n /** Maximum number of results to display */\n limit: number;\n /** Debounce delay in milliseconds */\n debounceMs: number;\n}\n\n/**\n * Search function type for filtering references\n */\nexport type SearchFunction = (query: string) => SearchResult[];\n\n/**\n * Result from the search prompt\n */\nexport interface SearchPromptResult {\n /** Selected references */\n selected: CslItem[];\n /** Whether the prompt was cancelled */\n cancelled: boolean;\n}\n\n/**\n * Maps internal choice value to CslItem\n */\ninterface ChoiceData {\n index: number;\n item: CslItem;\n}\n\n/**\n * Creates choices from search results\n */\nexport function createChoices(\n results: SearchResult[],\n terminalWidth: number\n): AutoCompleteChoice[] {\n return results.map((result, index) => {\n const displayIndex = index + 1;\n const formattedText = formatSearchResult(result.reference, displayIndex, terminalWidth);\n\n // Enquirer returns the 'name' property on selection when 'value' is not defined\n // So we store the JSON data in 'name' and use 'message' for display\n return {\n name: JSON.stringify({\n index,\n item: result.reference,\n } satisfies ChoiceData),\n message: formattedText,\n };\n });\n}\n\n/**\n * Parses selected values back to CslItems\n */\nexport function parseSelectedValues(values: string | string[]): CslItem[] {\n const valueArray = Array.isArray(values) ? values : [values];\n const items: CslItem[] = [];\n\n for (const value of valueArray) {\n if (!value) continue;\n try {\n const data = JSON.parse(value) as ChoiceData;\n items.push(data.item);\n } catch {\n // If parsing fails, the value might be just the id (name)\n // In this case, we can't recover the full item\n // This shouldn't happen in normal operation\n }\n }\n\n return items;\n}\n\n/**\n * Gets terminal width, falling back to 80 if not available\n */\nexport function getTerminalWidth(): number {\n return process.stdout.columns ?? 80;\n}\n\n/**\n * Gets terminal height, falling back to 24 if not available\n */\nexport function getTerminalHeight(): number {\n return process.stdout.rows ?? 24;\n}\n\n/**\n * Calculates the effective limit for the autocomplete list\n * based on terminal height to prevent input field from being hidden.\n * Reserves space for: prompt header (1), input line (1), footer hint (1), and padding (2)\n * Each item displays up to 3 lines (author/year, title, identifiers)\n */\nexport function calculateEffectiveLimit(configLimit: number): number {\n const terminalHeight = getTerminalHeight();\n const reservedLines = 5; // header + input + footer hint + padding\n const linesPerItem = 3; // each search result shows up to 3 lines\n const availableLines = terminalHeight - reservedLines;\n const maxVisibleChoices = Math.max(1, Math.floor(availableLines / linesPerItem));\n return configLimit > 0 ? Math.min(configLimit, maxVisibleChoices) : maxVisibleChoices;\n}\n\n/**\n * Collects enabled (selected) item IDs from choices\n */\nfunction collectEnabledIds(choices: AutoCompleteChoice[]): Set<string> {\n const enabledIds = new Set<string>();\n for (const choice of choices) {\n if ((choice as { enabled?: boolean }).enabled) {\n try {\n const data = JSON.parse(choice.name) as ChoiceData;\n enabledIds.add(data.item.id);\n } catch {\n // Ignore parse errors\n }\n }\n }\n return enabledIds;\n}\n\n/**\n * Restores enabled state for choices matching the given IDs\n */\nfunction restoreEnabledState(choices: AutoCompleteChoice[], enabledIds: Set<string>): void {\n for (const choice of choices) {\n try {\n const data = JSON.parse(choice.name) as ChoiceData;\n if (enabledIds.has(data.item.id)) {\n (choice as { enabled?: boolean }).enabled = true;\n }\n } catch {\n // Ignore parse errors\n }\n }\n}\n\n/**\n * Creates and runs an interactive search prompt\n */\nexport async function runSearchPrompt(\n allReferences: CslItem[],\n searchFn: SearchFunction,\n config: SearchPromptConfig,\n initialQuery = \"\"\n): Promise<SearchPromptResult> {\n // Dynamic import to allow mocking in tests\n // enquirer is a CommonJS module, so we must use default import\n const enquirer = await import(\"enquirer\");\n const BaseAutoComplete = (enquirer.default as unknown as Record<string, unknown>)\n .AutoComplete as new (\n options: Record<string, unknown>\n ) => {\n run(): Promise<string | string[]>;\n };\n\n const terminalWidth = getTerminalWidth();\n // Calculate effective limit based on terminal height\n const effectiveLimit = calculateEffectiveLimit(config.limit);\n\n // Create initial choices from all references (limited)\n const initialResults: SearchResult[] = initialQuery\n ? searchFn(initialQuery).slice(0, effectiveLimit)\n : allReferences.slice(0, effectiveLimit).map((ref) => ({\n reference: ref,\n overallStrength: \"exact\" as const,\n tokenMatches: [],\n score: 0,\n }));\n\n const initialChoices = createChoices(initialResults, terminalWidth);\n\n // Track last search query to avoid redundant searches\n let lastQuery = initialQuery;\n\n const promptOptions = {\n name: \"references\",\n message: \"Search references\",\n initial: initialQuery,\n choices: initialChoices,\n multiple: true,\n limit: effectiveLimit,\n footer: \"(Tab: select, Enter: confirm)\",\n suggest: (input: string, choices: AutoCompleteChoice[]) => {\n // If input hasn't changed, return current choices\n if (input === lastQuery) {\n return choices;\n }\n lastQuery = input;\n\n // Collect enabled (selected) item IDs from current choices\n const enabledIds = collectEnabledIds(choices);\n\n // Create new choices based on input\n const newChoices = input.trim()\n ? createChoices(searchFn(input).slice(0, effectiveLimit), terminalWidth)\n : createChoices(\n allReferences.slice(0, effectiveLimit).map((ref) => ({\n reference: ref,\n overallStrength: \"exact\" as const,\n tokenMatches: [],\n score: 0,\n })),\n terminalWidth\n );\n\n // Restore enabled state for matching items\n restoreEnabledState(newChoices, enabledIds);\n\n return newChoices;\n },\n };\n\n try {\n const prompt = new BaseAutoComplete(promptOptions);\n\n // Override key handlers:\n // - Space: append character instead of toggling selection\n // - Tab: toggle selection (like space does in default multiple mode)\n const promptInstance = prompt as unknown as {\n space: () => void;\n dispatch: (s: string, key: { name: string }) => Promise<void>;\n append: (ch: string) => void;\n toggle: (item: unknown) => void;\n focused: unknown;\n run(): Promise<string | string[]>;\n };\n\n promptInstance.space = function () {\n return this.append(\" \");\n };\n\n // Fix backspace at position 0 bug in enquirer\n // enquirer's delete() doesn't check if cursor is at position 0\n const promptWithDelete = promptInstance as unknown as {\n delete: () => unknown;\n cursor: number;\n input: string;\n alert: () => unknown;\n moveCursor: (n: number) => void;\n complete: () => Promise<void>;\n };\n if (typeof promptWithDelete.delete === \"function\") {\n const originalDelete = promptWithDelete.delete.bind(promptWithDelete);\n promptWithDelete.delete = function () {\n // Prevent deletion when cursor is at position 0\n if (this.cursor <= 0) {\n return this.alert();\n }\n return originalDelete();\n };\n }\n\n // Override next() which is called on Tab key press\n // Make it toggle selection only (no movement, use arrow keys to move)\n const promptWithRender = promptInstance as unknown as {\n next: () => void;\n render: () => void;\n };\n promptWithRender.next = function () {\n promptInstance.toggle(promptInstance.focused);\n this.render();\n };\n\n // Fix cursor position calculation bug in enquirer\n // enquirer's restore() uses input.length - cursor for cursor positioning,\n // but this doesn't account for the actual display width properly\n const promptWithRestore = promptInstance as unknown as {\n restore: () => void;\n sections: () => {\n header: string;\n prompt: string;\n after: string;\n rest: string[];\n last: string;\n };\n cursor: number;\n input: string;\n initial: string;\n value: string;\n state: { size: number; prompt: string };\n stdout: { write: (s: string) => void };\n options: { show?: boolean };\n };\n\n // Only override if restore method exists (it won't in test mocks)\n if (typeof promptWithRestore.restore === \"function\") {\n const originalRestore = promptWithRestore.restore.bind(promptWithRestore);\n promptWithRestore.restore = function () {\n if (this.options.show === false) return;\n\n const sections = this.sections();\n const size = sections.rest.length;\n this.state.size = size;\n\n if (size > 0) {\n // Move cursor up by 'size' lines and to the correct column position\n // Use the actual prompt from state (without input) for accurate positioning\n const basePrompt = this.state.prompt || \"\";\n const cursorPos = this.cursor;\n\n // Strip ANSI escape codes to get accurate display width\n // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape codes require control characters\n const stripAnsi = (str: string) => str.replace(/\\x1B\\[[0-9;]*[a-zA-Z]/g, \"\");\n const promptDisplayWidth = stripAnsi(basePrompt).length;\n\n // Calculate target column: base prompt length + cursor position in input\n // Add 1 because cursor.to() uses 0-indexed column but we want 1-indexed for ANSI\n const targetCol = promptDisplayWidth + cursorPos;\n\n // Build ANSI escape sequence\n let codes = \"\";\n codes += `\\u001b[${size}A`; // cursor up\n codes += `\\u001b[${targetCol + 1}G`; // cursor to column (1-indexed)\n\n this.stdout.write(codes);\n } else {\n // Fall back to original behavior for non-list cases\n originalRestore();\n }\n };\n }\n\n const result = await promptInstance.run();\n\n // Workaround for Enquirer bug: focused item with enabled=true may not be in result\n const promptAny = prompt as unknown as {\n focused?: { name: string; enabled?: boolean };\n selected?: Array<{ name: string }>;\n };\n\n let valuesToParse: string | string[] = result;\n\n // If result is empty but focused item is enabled, use focused item\n if (\n Array.isArray(result) &&\n result.length === 0 &&\n promptAny.focused?.enabled &&\n promptAny.focused?.name\n ) {\n valuesToParse = [promptAny.focused.name];\n }\n\n // Handle result\n const selected = parseSelectedValues(valuesToParse);\n\n return {\n selected,\n cancelled: false,\n };\n } catch (error) {\n // Enquirer throws an empty string when cancelled\n if (error === \"\" || (error instanceof Error && error.message === \"\")) {\n return {\n selected: [],\n cancelled: true,\n };\n }\n throw error;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { B as BUILTIN_STYLES } from "./index-4KSTJ3rp.js";
|
|
4
|
+
function expandTilde(filePath) {
|
|
5
|
+
if (filePath.startsWith("~/")) {
|
|
6
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
7
|
+
return path.join(home, filePath.slice(2));
|
|
8
|
+
}
|
|
9
|
+
return filePath;
|
|
10
|
+
}
|
|
11
|
+
function listCustomStyles(cslDirectory) {
|
|
12
|
+
if (!cslDirectory) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const directories = Array.isArray(cslDirectory) ? cslDirectory : [cslDirectory];
|
|
16
|
+
const styles = /* @__PURE__ */ new Set();
|
|
17
|
+
for (const dir of directories) {
|
|
18
|
+
const expandedDir = expandTilde(dir);
|
|
19
|
+
if (!fs.existsSync(expandedDir)) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const files = fs.readdirSync(expandedDir);
|
|
24
|
+
for (const file of files) {
|
|
25
|
+
if (file.endsWith(".csl")) {
|
|
26
|
+
styles.add(file.slice(0, -4));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return Array.from(styles).sort();
|
|
33
|
+
}
|
|
34
|
+
function buildStyleChoices(customStyles, defaultStyle = "apa") {
|
|
35
|
+
const choices = [];
|
|
36
|
+
const addedStyles = /* @__PURE__ */ new Set();
|
|
37
|
+
const addChoice = (styleName, isDefault) => {
|
|
38
|
+
if (addedStyles.has(styleName)) return;
|
|
39
|
+
addedStyles.add(styleName);
|
|
40
|
+
choices.push({
|
|
41
|
+
name: styleName,
|
|
42
|
+
message: isDefault ? `${styleName} (default)` : styleName,
|
|
43
|
+
value: styleName
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
addChoice(defaultStyle, true);
|
|
47
|
+
for (const style of BUILTIN_STYLES) {
|
|
48
|
+
addChoice(style, false);
|
|
49
|
+
}
|
|
50
|
+
for (const style of customStyles) {
|
|
51
|
+
addChoice(style, false);
|
|
52
|
+
}
|
|
53
|
+
return choices;
|
|
54
|
+
}
|
|
55
|
+
async function runStyleSelect(options) {
|
|
56
|
+
const enquirer = await import("enquirer");
|
|
57
|
+
const Select = enquirer.default.Select;
|
|
58
|
+
const customStyles = listCustomStyles(options.cslDirectory);
|
|
59
|
+
const choices = buildStyleChoices(customStyles, options.defaultStyle);
|
|
60
|
+
const promptOptions = {
|
|
61
|
+
name: "style",
|
|
62
|
+
message: "Select citation style:",
|
|
63
|
+
choices
|
|
64
|
+
};
|
|
65
|
+
try {
|
|
66
|
+
const prompt = new Select(promptOptions);
|
|
67
|
+
const result = await prompt.run();
|
|
68
|
+
return {
|
|
69
|
+
style: result,
|
|
70
|
+
cancelled: false
|
|
71
|
+
};
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (error === "" || error instanceof Error && error.message === "") {
|
|
74
|
+
return {
|
|
75
|
+
cancelled: true
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export {
|
|
82
|
+
buildStyleChoices,
|
|
83
|
+
listCustomStyles,
|
|
84
|
+
runStyleSelect
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=style-select-CHjDTyq2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style-select-CHjDTyq2.js","sources":["../../src/features/interactive/style-select.ts"],"sourcesContent":["/**\n * Style selection prompt for citation style selection.\n *\n * Lists built-in styles and custom styles from csl_directory,\n * with the default style shown first.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { BUILTIN_STYLES } from \"../../config/csl-styles.js\";\n\n/**\n * Options for style selection\n */\nexport interface StyleSelectOptions {\n /** Directory or directories containing custom CSL files */\n cslDirectory?: string | string[];\n /** Default style to show first */\n defaultStyle?: string;\n}\n\n/**\n * Result from style selection\n */\nexport interface StyleSelectResult {\n /** Selected style name */\n style?: string;\n /** Whether the selection was cancelled */\n cancelled: boolean;\n}\n\n/**\n * Choice definition for Enquirer Select prompt\n */\ninterface StyleChoice {\n name: string;\n message: string;\n value: string;\n}\n\n/**\n * Expand tilde (~) in path to home directory\n */\nfunction expandTilde(filePath: string): string {\n if (filePath.startsWith(\"~/\")) {\n const home = process.env.HOME || process.env.USERPROFILE || \"\";\n return path.join(home, filePath.slice(2));\n }\n return filePath;\n}\n\n/**\n * List custom CSL style names from the specified directory/directories.\n *\n * @param cslDirectory - Directory or directories to search for CSL files\n * @returns Array of style names (without .csl extension)\n */\nexport function listCustomStyles(cslDirectory: string | string[] | undefined): string[] {\n if (!cslDirectory) {\n return [];\n }\n\n const directories = Array.isArray(cslDirectory) ? cslDirectory : [cslDirectory];\n const styles = new Set<string>();\n\n for (const dir of directories) {\n const expandedDir = expandTilde(dir);\n\n if (!fs.existsSync(expandedDir)) {\n continue;\n }\n\n try {\n const files = fs.readdirSync(expandedDir);\n for (const file of files) {\n if (file.endsWith(\".csl\")) {\n styles.add(file.slice(0, -4)); // Remove .csl extension\n }\n }\n } catch {\n // Ignore read errors\n }\n }\n\n return Array.from(styles).sort();\n}\n\n/**\n * Build style choices for the selection prompt.\n *\n * @param customStyles - Custom style names from csl_directory\n * @param defaultStyle - Default style to show first\n * @returns Array of choices for Enquirer Select prompt\n */\nexport function buildStyleChoices(customStyles: string[], defaultStyle = \"apa\"): StyleChoice[] {\n const choices: StyleChoice[] = [];\n const addedStyles = new Set<string>();\n\n // Helper to add a style choice\n const addChoice = (styleName: string, isDefault: boolean) => {\n if (addedStyles.has(styleName)) return;\n addedStyles.add(styleName);\n\n choices.push({\n name: styleName,\n message: isDefault ? `${styleName} (default)` : styleName,\n value: styleName,\n });\n };\n\n // Add default style first\n addChoice(defaultStyle, true);\n\n // Add built-in styles\n for (const style of BUILTIN_STYLES) {\n addChoice(style, false);\n }\n\n // Add custom styles (excluding any that match built-in names)\n for (const style of customStyles) {\n addChoice(style, false);\n }\n\n return choices;\n}\n\n/**\n * Run the style selection prompt.\n *\n * @param options - Style selection options\n * @returns Selection result with style name\n */\nexport async function runStyleSelect(options: StyleSelectOptions): Promise<StyleSelectResult> {\n // Dynamic import to allow mocking in tests\n const enquirer = await import(\"enquirer\");\n const Select = (enquirer.default as unknown as Record<string, unknown>).Select as new (\n opts: Record<string, unknown>\n ) => { run(): Promise<string> };\n\n // List custom styles from csl_directory\n const customStyles = listCustomStyles(options.cslDirectory);\n\n // Build choices with default first\n const choices = buildStyleChoices(customStyles, options.defaultStyle);\n\n const promptOptions = {\n name: \"style\",\n message: \"Select citation style:\",\n choices,\n };\n\n try {\n const prompt = new Select(promptOptions);\n const result = await prompt.run();\n\n return {\n style: result,\n cancelled: false,\n };\n } catch (error) {\n // Enquirer throws an empty string when cancelled\n if (error === \"\" || (error instanceof Error && error.message === \"\")) {\n return {\n cancelled: true,\n };\n }\n throw error;\n }\n}\n"],"names":[],"mappings":";;;AA2CA,SAAS,YAAY,UAA0B;AAC7C,MAAI,SAAS,WAAW,IAAI,GAAG;AAC7B,UAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,WAAO,KAAK,KAAK,MAAM,SAAS,MAAM,CAAC,CAAC;AAAA,EAC1C;AACA,SAAO;AACT;AAQO,SAAS,iBAAiB,cAAuD;AACtF,MAAI,CAAC,cAAc;AACjB,WAAO,CAAA;AAAA,EACT;AAEA,QAAM,cAAc,MAAM,QAAQ,YAAY,IAAI,eAAe,CAAC,YAAY;AAC9E,QAAM,6BAAa,IAAA;AAEnB,aAAW,OAAO,aAAa;AAC7B,UAAM,cAAc,YAAY,GAAG;AAEnC,QAAI,CAAC,GAAG,WAAW,WAAW,GAAG;AAC/B;AAAA,IACF;AAEA,QAAI;AACF,YAAM,QAAQ,GAAG,YAAY,WAAW;AACxC,iBAAW,QAAQ,OAAO;AACxB,YAAI,KAAK,SAAS,MAAM,GAAG;AACzB,iBAAO,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC;AAAA,QAC9B;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,MAAM,EAAE,KAAA;AAC5B;AASO,SAAS,kBAAkB,cAAwB,eAAe,OAAsB;AAC7F,QAAM,UAAyB,CAAA;AAC/B,QAAM,kCAAkB,IAAA;AAGxB,QAAM,YAAY,CAAC,WAAmB,cAAuB;AAC3D,QAAI,YAAY,IAAI,SAAS,EAAG;AAChC,gBAAY,IAAI,SAAS;AAEzB,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,SAAS,YAAY,GAAG,SAAS,eAAe;AAAA,MAChD,OAAO;AAAA,IAAA,CACR;AAAA,EACH;AAGA,YAAU,cAAc,IAAI;AAG5B,aAAW,SAAS,gBAAgB;AAClC,cAAU,OAAO,KAAK;AAAA,EACxB;AAGA,aAAW,SAAS,cAAc;AAChC,cAAU,OAAO,KAAK;AAAA,EACxB;AAEA,SAAO;AACT;AAQA,eAAsB,eAAe,SAAyD;AAE5F,QAAM,WAAW,MAAM,OAAO,UAAU;AACxC,QAAM,SAAU,SAAS,QAA+C;AAKxE,QAAM,eAAe,iBAAiB,QAAQ,YAAY;AAG1D,QAAM,UAAU,kBAAkB,cAAc,QAAQ,YAAY;AAEpE,QAAM,gBAAgB;AAAA,IACpB,MAAM;AAAA,IACN,SAAS;AAAA,IACT;AAAA,EAAA;AAGF,MAAI;AACF,UAAM,SAAS,IAAI,OAAO,aAAa;AACvC,UAAM,SAAS,MAAM,OAAO,IAAA;AAE5B,WAAO;AAAA,MACL,OAAO;AAAA,MACP,WAAW;AAAA,IAAA;AAAA,EAEf,SAAS,OAAO;AAEd,QAAI,UAAU,MAAO,iBAAiB,SAAS,MAAM,YAAY,IAAK;AACpE,aAAO;AAAA,QACL,WAAW;AAAA,MAAA;AAAA,IAEf;AACA,UAAM;AAAA,EACR;AACF;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CiteResult } from "../../features/operations/cite.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type ExecutionContext } from "../execution-context.js";
|
|
3
3
|
/**
|
|
4
4
|
* Options for the cite command.
|
|
5
5
|
*/
|
|
@@ -46,4 +46,8 @@ export declare function formatCiteErrors(result: CiteCommandResult): string;
|
|
|
46
46
|
* @returns Exit code (0 for success/partial success, 1 for complete failure)
|
|
47
47
|
*/
|
|
48
48
|
export declare function getCiteExitCode(result: CiteCommandResult): number;
|
|
49
|
+
/**
|
|
50
|
+
* Handle 'cite' command action.
|
|
51
|
+
*/
|
|
52
|
+
export declare function handleCiteAction(identifiers: string[], options: Omit<CiteCommandOptions, "identifiers">, globalOpts: Record<string, unknown>): Promise<void>;
|
|
49
53
|
//# sourceMappingURL=cite.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cite.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/cite.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cite.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/cite.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAwB,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAC1F,OAAO,EAAE,KAAK,gBAAgB,EAA0B,MAAM,yBAAyB,CAAC;AAGxF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,UAAU,CAAC;AAiC3C;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,iBAAiB,CAAC,CAI5B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAQlE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAQlE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAWjE;AAoCD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EAAE,EACrB,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,aAAa,CAAC,EAChD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,OAAO,CAAC,IAAI,CAAC,CAyCf"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Opens references in external editor for interactive editing.
|
|
5
5
|
*/
|
|
6
6
|
import { type EditFormat } from "../../features/edit/index.js";
|
|
7
|
-
import type
|
|
7
|
+
import { type ExecutionContext } from "../execution-context.js";
|
|
8
8
|
/**
|
|
9
9
|
* Options for the edit command.
|
|
10
10
|
*/
|
|
@@ -43,4 +43,16 @@ export declare function executeEditCommand(options: EditCommandOptions, context:
|
|
|
43
43
|
* @returns Formatted output string
|
|
44
44
|
*/
|
|
45
45
|
export declare function formatEditOutput(result: EditCommandResult): string;
|
|
46
|
+
/**
|
|
47
|
+
* Options for handleEditAction.
|
|
48
|
+
*/
|
|
49
|
+
export interface EditActionOptions {
|
|
50
|
+
uuid?: boolean;
|
|
51
|
+
format?: EditFormat;
|
|
52
|
+
editor?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Handle 'edit' command action.
|
|
56
|
+
*/
|
|
57
|
+
export declare function handleEditAction(identifiers: string[], options: EditActionOptions, globalOpts: Record<string, unknown>): Promise<void>;
|
|
46
58
|
//# sourceMappingURL=edit.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/edit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/edit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,EAAE,KAAK,UAAU,EAA8B,MAAM,8BAA8B,CAAC;AAC3F,OAAO,EAAE,KAAK,gBAAgB,EAA0B,MAAM,yBAAyB,CAAC;AAGxF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,0CAA0C;IAC1C,MAAM,EAAE,UAAU,CAAC;IACnB,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAyGD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,iBAAiB,CAAC,CAmD5B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAsBlE;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAgCD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EAAE,EACrB,OAAO,EAAE,iBAAiB,EAC1B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,OAAO,CAAC,IAAI,CAAC,CAqDf"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import type { IdentifierType } from "../../core/library-interface.js";
|
|
7
7
|
import type { FulltextType } from "../../features/fulltext/index.js";
|
|
8
8
|
import { type FulltextAttachResult, type FulltextDetachResult, type FulltextGetResult, type FulltextOpenResult } from "../../features/operations/fulltext/index.js";
|
|
9
|
-
import type
|
|
9
|
+
import { type ExecutionContext } from "../execution-context.js";
|
|
10
10
|
/**
|
|
11
11
|
* Options for fulltext attach command
|
|
12
12
|
*/
|
|
@@ -87,4 +87,57 @@ export declare function formatFulltextOpenOutput(result: FulltextOpenResult): st
|
|
|
87
87
|
* Get exit code for fulltext command result
|
|
88
88
|
*/
|
|
89
89
|
export declare function getFulltextExitCode(result: FulltextAttachResult | FulltextGetResult | FulltextDetachResult | FulltextOpenResult): number;
|
|
90
|
+
/**
|
|
91
|
+
* Options for fulltext attach action
|
|
92
|
+
*/
|
|
93
|
+
export interface FulltextAttachActionOptions {
|
|
94
|
+
pdf?: string | boolean;
|
|
95
|
+
markdown?: string | boolean;
|
|
96
|
+
move?: boolean;
|
|
97
|
+
force?: boolean;
|
|
98
|
+
uuid?: boolean;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Handle 'fulltext attach' command action.
|
|
102
|
+
*/
|
|
103
|
+
export declare function handleFulltextAttachAction(identifierArg: string | undefined, filePathArg: string | undefined, options: FulltextAttachActionOptions, globalOpts: Record<string, unknown>): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Options for fulltext get action
|
|
106
|
+
*/
|
|
107
|
+
export interface FulltextGetActionOptions {
|
|
108
|
+
pdf?: boolean;
|
|
109
|
+
markdown?: boolean;
|
|
110
|
+
stdout?: boolean;
|
|
111
|
+
uuid?: boolean;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Handle 'fulltext get' command action.
|
|
115
|
+
*/
|
|
116
|
+
export declare function handleFulltextGetAction(identifierArg: string | undefined, options: FulltextGetActionOptions, globalOpts: Record<string, unknown>): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Options for fulltext detach action
|
|
119
|
+
*/
|
|
120
|
+
export interface FulltextDetachActionOptions {
|
|
121
|
+
pdf?: boolean;
|
|
122
|
+
markdown?: boolean;
|
|
123
|
+
delete?: boolean;
|
|
124
|
+
force?: boolean;
|
|
125
|
+
uuid?: boolean;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Handle 'fulltext detach' command action.
|
|
129
|
+
*/
|
|
130
|
+
export declare function handleFulltextDetachAction(identifierArg: string | undefined, options: FulltextDetachActionOptions, globalOpts: Record<string, unknown>): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Options for fulltext open action
|
|
133
|
+
*/
|
|
134
|
+
export interface FulltextOpenActionOptions {
|
|
135
|
+
pdf?: boolean;
|
|
136
|
+
markdown?: boolean;
|
|
137
|
+
uuid?: boolean;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Handle 'fulltext open' command action.
|
|
141
|
+
*/
|
|
142
|
+
export declare function handleFulltextOpenAction(identifierArg: string | undefined, options: FulltextOpenActionOptions, globalOpts: Record<string, unknown>): Promise<void>;
|
|
90
143
|
//# sourceMappingURL=fulltext.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fulltext.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/fulltext.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"fulltext.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/fulltext.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { IdentifierType } from "../../core/library-interface.js";
|
|
2
2
|
import type { FulltextType } from "../../features/fulltext/index.js";
|
|
3
3
|
import { type RemoveResult, getFulltextAttachmentTypes } from "../../features/operations/remove.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type ExecutionContext } from "../execution-context.js";
|
|
5
5
|
export { getFulltextAttachmentTypes };
|
|
6
6
|
export type { RemoveResult };
|
|
7
7
|
/**
|
|
@@ -43,4 +43,17 @@ export declare function formatRemoveOutput(result: RemoveCommandResult, identifi
|
|
|
43
43
|
* @returns Warning message string
|
|
44
44
|
*/
|
|
45
45
|
export declare function formatFulltextWarning(types: FulltextType[]): string;
|
|
46
|
+
/**
|
|
47
|
+
* Options for handleRemoveAction.
|
|
48
|
+
*/
|
|
49
|
+
export interface RemoveActionOptions {
|
|
50
|
+
uuid?: boolean;
|
|
51
|
+
force?: boolean;
|
|
52
|
+
output?: "json" | "text";
|
|
53
|
+
full?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Handle 'remove' command action.
|
|
57
|
+
*/
|
|
58
|
+
export declare function handleRemoveAction(identifierArg: string | undefined, options: RemoveActionOptions, globalOpts: Record<string, unknown>): Promise<void>;
|
|
46
59
|
//# sourceMappingURL=remove.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/remove.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/remove.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,KAAK,YAAY,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACpG,OAAO,EAAE,KAAK,gBAAgB,EAA0B,MAAM,yBAAyB,CAAC;AASxF,OAAO,EAAE,0BAA0B,EAAE,CAAC;AACtC,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,0CAA0C;IAC1C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kDAAkD;IAClD,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC;AAE/C;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAqB9B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAqB1F;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,CAInE;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAgID;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,OAAO,EAAE,mBAAmB,EAC5B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CslItem } from "../../core/csl-json/types.js";
|
|
2
2
|
import type { IdentifierType } from "../../core/library-interface.js";
|
|
3
3
|
import type { UpdateOperationResult } from "../../features/operations/update.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type ExecutionContext } from "../execution-context.js";
|
|
5
5
|
/**
|
|
6
6
|
* Operator type for --set option.
|
|
7
7
|
*/
|
|
@@ -71,4 +71,21 @@ export declare function executeUpdate(options: UpdateCommandOptions, context: Ex
|
|
|
71
71
|
* @returns Formatted output string
|
|
72
72
|
*/
|
|
73
73
|
export declare function formatUpdateOutput(result: UpdateCommandResult, identifier: string): string;
|
|
74
|
+
/**
|
|
75
|
+
* Options for handleUpdateAction.
|
|
76
|
+
*/
|
|
77
|
+
export interface UpdateActionOptions {
|
|
78
|
+
uuid?: boolean;
|
|
79
|
+
set?: string[];
|
|
80
|
+
output?: "json" | "text";
|
|
81
|
+
full?: boolean;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Handle 'update' command action.
|
|
85
|
+
*/
|
|
86
|
+
export declare function handleUpdateAction(identifierArg: string | undefined, file: string | undefined, options: UpdateActionOptions, globalOpts: Record<string, unknown>): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Collect multiple --set options into an array.
|
|
89
|
+
*/
|
|
90
|
+
export declare function collectSetOption(value: string, previous: string[]): string[];
|
|
74
91
|
//# sourceMappingURL=update.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/update.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/update.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,KAAK,gBAAgB,EAA0B,MAAM,yBAAyB,CAAC;AASxF;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAiB1D;AAoID;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMtF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAQ5E;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,YAAY,EAAE,OAAO,GACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBzB;AAmDD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1B,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,CAAC;AAExD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAwB9B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAsB1F;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAkHD;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,EAAE,mBAAmB,EAC5B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAE5E"}
|
package/dist/cli/helpers.d.ts
CHANGED
|
@@ -55,6 +55,17 @@ export declare function getOutputFormat(options: CliOptions): OutputFormat;
|
|
|
55
55
|
* Set REF_SKIP_TTY_CHECK=1 environment variable to skip TTY check (for testing only)
|
|
56
56
|
*/
|
|
57
57
|
export declare function isTTY(): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Read identifiers from stdin (for non-TTY/pipeline mode).
|
|
60
|
+
* Reads all lines, splits by whitespace/newlines, filters empty.
|
|
61
|
+
* Returns empty array if stdin has no content.
|
|
62
|
+
*/
|
|
63
|
+
export declare function readIdentifiersFromStdin(): Promise<string[]>;
|
|
64
|
+
/**
|
|
65
|
+
* Read a single identifier from stdin (for non-TTY/pipeline mode).
|
|
66
|
+
* Returns the first non-empty line, or undefined if stdin is empty.
|
|
67
|
+
*/
|
|
68
|
+
export declare function readIdentifierFromStdin(): Promise<string | undefined>;
|
|
58
69
|
/**
|
|
59
70
|
* Read confirmation from user (y/N)
|
|
60
71
|
* @param prompt - Confirmation prompt message
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/cli/helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBlE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAYrD;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgClF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,GAAG,YAAY,CAejE;AAED;;;;;;GAMG;AACH,wBAAgB,KAAK,IAAI,OAAO,CAK/B;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/cli/helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBlE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAYrD;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgClF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,GAAG,YAAY,CAejE;AAED;;;;;;GAMG;AACH,wBAAgB,KAAK,IAAI,OAAO,CAK/B;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAMlE;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAI3E;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA0BvE;AAED;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CASzD;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAMxD;AAED;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CAMvD"}
|
package/dist/cli/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyCpC;;GAEG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAiCvC;AAsiBD;;GAEG;AACH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBxD"}
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import "commander";
|
|
2
|
-
import "
|
|
3
|
-
import { c, m } from "./chunks/index-CaAOawzv.js";
|
|
2
|
+
import { c, m } from "./chunks/index-UpzsmbyY.js";
|
|
4
3
|
import "./chunks/file-watcher-D2Y-SlcE.js";
|
|
5
|
-
import "./chunks/index-4KSTJ3rp.js";
|
|
6
4
|
export {
|
|
7
5
|
c as createProgram,
|
|
8
6
|
m as main
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|