@ncukondo/reference-manager 0.31.0 → 0.32.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/README.md +20 -0
- package/dist/chunks/{SearchableMultiSelect-BFgiGVkF.js → SearchableMultiSelect-jAKQd_B0.js} +2 -2
- package/dist/chunks/{SearchableMultiSelect-BFgiGVkF.js.map → SearchableMultiSelect-jAKQd_B0.js.map} +1 -1
- package/dist/chunks/{action-menu-D5s90TfM.js → action-menu-DLEwSKrj.js} +3 -3
- package/dist/chunks/{action-menu-D5s90TfM.js.map → action-menu-DLEwSKrj.js.map} +1 -1
- package/dist/chunks/{checker-DMyZFCaP.js → checker-kVM4S67y.js} +4 -4
- package/dist/chunks/{checker-DMyZFCaP.js.map → checker-kVM4S67y.js.map} +1 -1
- package/dist/chunks/{crossref-client-CaFCAASM.js → crossref-client-DcJ42Qt6.js} +2 -2
- package/dist/chunks/{crossref-client-CaFCAASM.js.map → crossref-client-DcJ42Qt6.js.map} +1 -1
- package/dist/chunks/{fix-interaction-h15RI_Ae.js → fix-interaction-DWUzp9Ri.js} +5 -5
- package/dist/chunks/{fix-interaction-h15RI_Ae.js.map → fix-interaction-DWUzp9Ri.js.map} +1 -1
- package/dist/chunks/{index-CS8S6gE4.js → index-B4-i4PrU.js} +3 -3
- package/dist/chunks/{index-CS8S6gE4.js.map → index-B4-i4PrU.js.map} +1 -1
- package/dist/chunks/{index-Cqss7VR_.js → index-B8iEozpf.js} +3 -3
- package/dist/chunks/index-B8iEozpf.js.map +1 -0
- package/dist/chunks/{index-BG3Ef4Pd.js → index-BuQm8A5F.js} +4 -4
- package/dist/chunks/{index-BG3Ef4Pd.js.map → index-BuQm8A5F.js.map} +1 -1
- package/dist/chunks/{index-DCBYzNj-.js → index-CmkN2-2A.js} +149 -55
- package/dist/chunks/index-CmkN2-2A.js.map +1 -0
- package/dist/chunks/{pubmed-client-vbreWUxK.js → pubmed-client-Bhzz4Gg5.js} +2 -2
- package/dist/chunks/{pubmed-client-vbreWUxK.js.map → pubmed-client-Bhzz4Gg5.js.map} +1 -1
- package/dist/chunks/{reference-select-CNWexQGu.js → reference-select-B9b9Ez7Q.js} +3 -3
- package/dist/chunks/{reference-select-CNWexQGu.js.map → reference-select-B9b9Ez7Q.js.map} +1 -1
- package/dist/chunks/{style-select-D8beMum_.js → style-select-aXByJLOo.js} +3 -3
- package/dist/chunks/{style-select-D8beMum_.js.map → style-select-aXByJLOo.js.map} +1 -1
- package/dist/cli/commands/install/install.d.ts +11 -0
- package/dist/cli/commands/install/install.d.ts.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/features/install/index.d.ts +6 -0
- package/dist/features/install/index.d.ts.map +1 -0
- package/dist/features/install/write-skills.d.ts +21 -0
- package/dist/features/install/write-skills.d.ts.map +1 -0
- package/dist/server.js +1 -1
- package/package.json +1 -1
- package/dist/chunks/index-Cqss7VR_.js.map +0 -1
- package/dist/chunks/index-DCBYzNj-.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a } from "./index-
|
|
2
|
-
import { d, g, l, o, s } from "./index-
|
|
1
|
+
import { a } from "./index-B4-i4PrU.js";
|
|
2
|
+
import { d, g, l, o, s } from "./index-CmkN2-2A.js";
|
|
3
3
|
export {
|
|
4
4
|
a as addAttachment,
|
|
5
5
|
d as detachAttachment,
|
|
@@ -8,4 +8,4 @@ export {
|
|
|
8
8
|
o as openAttachment,
|
|
9
9
|
s as syncAttachments
|
|
10
10
|
};
|
|
11
|
-
//# sourceMappingURL=index-
|
|
11
|
+
//# sourceMappingURL=index-B8iEozpf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-B8iEozpf.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useApp, Box, render } from "ink";
|
|
2
2
|
import { useState, useEffect, createElement } from "react";
|
|
3
|
-
import { getActionChoices, OUTPUT_FORMAT_CHOICES, STYLE_CHOICES, isSideEffectAction, generateOutput } from "./action-menu-
|
|
4
|
-
import { S as SearchableMultiSelect, t as toChoice$1, c as calculateEffectiveLimit, f as formatAuthors, a as formatIdentifiers } from "./SearchableMultiSelect-
|
|
5
|
-
import { S as Select, r as restoreStdinAfterInk } from "./index-
|
|
3
|
+
import { getActionChoices, OUTPUT_FORMAT_CHOICES, STYLE_CHOICES, isSideEffectAction, generateOutput } from "./action-menu-DLEwSKrj.js";
|
|
4
|
+
import { S as SearchableMultiSelect, t as toChoice$1, c as calculateEffectiveLimit, f as formatAuthors, a as formatIdentifiers } from "./SearchableMultiSelect-jAKQd_B0.js";
|
|
5
|
+
import { S as Select, r as restoreStdinAfterInk } from "./index-CmkN2-2A.js";
|
|
6
6
|
function SearchFlowApp({
|
|
7
7
|
choices,
|
|
8
8
|
filterFn,
|
|
@@ -392,4 +392,4 @@ export {
|
|
|
392
392
|
runCiteFlow,
|
|
393
393
|
runSearchFlow
|
|
394
394
|
};
|
|
395
|
-
//# sourceMappingURL=index-
|
|
395
|
+
//# sourceMappingURL=index-BuQm8A5F.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-BG3Ef4Pd.js","sources":["../../src/features/interactive/apps/SearchFlowApp.tsx","../../src/features/interactive/apps/runSearchFlow.ts","../../src/features/interactive/apps/CiteFlowApp.tsx","../../src/features/interactive/apps/runCiteFlow.ts"],"sourcesContent":["/**\n * SearchFlowApp - Single App for search -t flow\n *\n * Manages state transitions: search → action → (style/output-format if needed)\n * Following React Ink Single App Pattern (ADR-015)\n */\n\nimport { Box, useApp } from \"ink\";\nimport type React from \"react\";\nimport { createElement, useEffect, useState } from \"react\";\nimport type { CitationKeyFormat } from \"../../../config/schema.js\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport {\n type ActionMenuResult,\n type ActionType,\n OUTPUT_FORMAT_CHOICES,\n type OutputFormatType,\n STYLE_CHOICES,\n generateOutput,\n getActionChoices,\n isSideEffectAction,\n} from \"../action-menu.js\";\nimport {\n type Choice,\n SearchableMultiSelect,\n Select,\n type SortOption,\n} from \"../components/index.js\";\n\n/**\n * Flow states for the search flow\n */\ntype FlowState = \"search\" | \"action\" | \"style\" | \"output-format\" | \"exiting\";\n\n/**\n * Props for SearchFlowApp\n */\nexport interface SearchFlowAppProps {\n /** Choices for the search prompt */\n choices: Choice<CslItem>[];\n /** Filter function for search */\n filterFn: (query: string, choices: Choice<CslItem>[]) => Choice<CslItem>[];\n /** Number of visible items */\n visibleCount: number;\n /** Default sort option */\n defaultSort: SortOption;\n /** Default citation key format */\n defaultKeyFormat: CitationKeyFormat;\n /** Default citation style */\n defaultStyle: string;\n /** Debounce delay in milliseconds for search filtering */\n debounceMs?: number;\n /** Callback when flow completes */\n onComplete: (result: ActionMenuResult) => void;\n /** Callback when flow is cancelled */\n onCancel: () => void;\n}\n\n/**\n * SearchFlowApp component\n *\n * Single App that manages search → action → style/output-format flow\n */\nexport function SearchFlowApp({\n choices,\n filterFn,\n visibleCount,\n defaultSort,\n defaultKeyFormat,\n defaultStyle,\n debounceMs,\n onComplete,\n onCancel,\n}: SearchFlowAppProps): React.ReactElement {\n const { exit } = useApp();\n const [state, setState] = useState<FlowState>(\"search\");\n const [selectedItems, setSelectedItems] = useState<CslItem[]>([]);\n const [pendingResult, setPendingResult] = useState<ActionMenuResult | null>(null);\n\n // Exit when entering \"exiting\" state (after rendering empty component)\n useEffect(() => {\n if (state === \"exiting\" && pendingResult) {\n exit();\n if (pendingResult.cancelled) {\n onCancel();\n } else {\n onComplete(pendingResult);\n }\n }\n }, [state, pendingResult, exit, onCancel, onComplete]);\n\n // Transition to exiting state with result\n const exitWith = (result: ActionMenuResult) => {\n setPendingResult(result);\n setState(\"exiting\");\n };\n\n // Handle search submission\n const handleSearchSubmit = (selected: Choice<CslItem>[]) => {\n if (selected.length === 0) {\n exitWith({ action: \"cancel\", output: \"\", cancelled: true });\n return;\n }\n setSelectedItems(selected.map((c) => c.value));\n setState(\"action\");\n };\n\n // Handle search cancel\n const handleSearchCancel = () => {\n exitWith({ action: \"cancel\", output: \"\", cancelled: true });\n };\n\n // Handle action selection\n const handleActionSelect = (action: ActionType) => {\n if (action === \"cancel\") {\n exitWith({ action: \"cancel\", output: \"\", cancelled: true });\n return;\n }\n\n // If cite-choose, go to style selection\n if (action === \"cite-choose\") {\n setState(\"style\");\n return;\n }\n\n // If output-format, go to output format submenu\n if (action === \"output-format\") {\n setState(\"output-format\");\n return;\n }\n\n // Handle side-effect actions\n if (isSideEffectAction(action)) {\n exitWith({ action, output: \"\", cancelled: false, selectedItems });\n return;\n }\n\n // Generate output and complete\n const output = generateOutput(action, selectedItems, {\n defaultKeyFormat,\n defaultStyle,\n });\n exitWith({ action, output, cancelled: false });\n };\n\n // Handle action cancel (go back to search)\n const handleActionCancel = () => {\n setState(\"search\");\n };\n\n // Handle style selection\n const handleStyleSelect = (style: string) => {\n const output = generateOutput(\"cite-choose\", selectedItems, {\n defaultKeyFormat,\n defaultStyle: style,\n });\n exitWith({ action: \"cite-choose\", output, cancelled: false });\n };\n\n // Handle style cancel (go back to action)\n const handleStyleCancel = () => {\n setState(\"action\");\n };\n\n // Handle output format selection\n const handleOutputFormatSelect = (format: OutputFormatType) => {\n if (format === \"cancel\") {\n setState(\"action\");\n return;\n }\n\n const output = generateOutput(format, selectedItems, {\n defaultKeyFormat,\n defaultStyle,\n });\n exitWith({ action: \"output-format\", output, cancelled: false });\n };\n\n // Handle output format cancel (go back to action)\n const handleOutputFormatCancel = () => {\n setState(\"action\");\n };\n\n // Render based on current state\n if (state === \"exiting\") {\n // Empty component - Ink will clear the previous content\n return createElement(Box);\n }\n\n if (state === \"search\") {\n return createElement(SearchableMultiSelect<CslItem>, {\n choices,\n filterFn,\n visibleCount,\n onSubmit: handleSearchSubmit,\n onCancel: handleSearchCancel,\n header: \"Search references\",\n placeholder: \"Type to search...\",\n defaultSort,\n ...(debounceMs !== undefined && { debounceMs }),\n });\n }\n\n if (state === \"action\") {\n const count = selectedItems.length;\n const refWord = count === 1 ? \"reference\" : \"references\";\n return createElement(Select<ActionType>, {\n key: \"action\",\n options: getActionChoices(count, { defaultKeyFormat }),\n message: `Action for ${count} selected ${refWord}:`,\n onSelect: handleActionSelect,\n onCancel: handleActionCancel,\n });\n }\n\n if (state === \"output-format\") {\n return createElement(Select<OutputFormatType>, {\n key: \"output-format\",\n options: OUTPUT_FORMAT_CHOICES,\n message: \"Select output format:\",\n onSelect: handleOutputFormatSelect,\n onCancel: handleOutputFormatCancel,\n });\n }\n\n // state === \"style\"\n return createElement(Select<string>, {\n key: \"style\",\n options: STYLE_CHOICES,\n message: \"Select citation style:\",\n onSelect: handleStyleSelect,\n onCancel: handleStyleCancel,\n });\n}\n","/**\n * Runner for SearchFlowApp\n *\n * Provides the public API for running the search flow.\n */\n\nimport { render } from \"ink\";\nimport { createElement } from \"react\";\nimport type { CitationKeyFormat } from \"../../../config/schema.js\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport type { SearchResult } from \"../../search/types.js\";\nimport type { ActionMenuResult } from \"../action-menu.js\";\nimport { restoreStdinAfterInk } from \"../alternate-screen.js\";\nimport { toChoice } from \"../choice-builder.js\";\nimport { type Choice, type SortOption, calculateEffectiveLimit } from \"../components/index.js\";\nimport { SearchFlowApp } from \"./SearchFlowApp.js\";\n\n/**\n * Configuration for the search flow\n */\nexport interface SearchFlowConfig {\n /** Maximum number of results to display */\n limit: number;\n /** Debounce delay in milliseconds for search filtering */\n debounceMs: number;\n /** Default citation key format */\n defaultKeyFormat?: CitationKeyFormat;\n /** Default citation style */\n defaultStyle?: string;\n}\n\n/**\n * Search function type for filtering references\n */\nexport type SearchFunction = (query: string) => SearchResult[];\n\n/**\n * Run the search flow (search → action → style if needed)\n *\n * This is the main entry point for the `search -t` command.\n */\nexport async function runSearchFlow(\n allReferences: CslItem[],\n searchFn: SearchFunction,\n config: SearchFlowConfig\n): Promise<ActionMenuResult> {\n // Convert references to choices and build lookup map\n const choices = allReferences.map(toChoice);\n const choiceMap = new Map(choices.map((c) => [c.id, c]));\n\n // Calculate effective visible count\n const effectiveLimit = calculateEffectiveLimit(config.limit);\n\n // Create filter function using the provided search function\n const filterFn = (query: string, choices: Choice<CslItem>[]): Choice<CslItem>[] => {\n if (!query.trim()) return choices;\n\n const results = searchFn(query);\n return results.flatMap((r) => {\n const choice = choiceMap.get(r.reference.id);\n return choice ? [choice] : [];\n });\n };\n\n // Default sort option\n const defaultSort: SortOption = \"updated-desc\";\n\n // Create a promise to capture the result\n return new Promise<ActionMenuResult>((resolve) => {\n let flowResult: ActionMenuResult = {\n action: \"cancel\",\n output: \"\",\n cancelled: true,\n };\n\n const handleComplete = (result: ActionMenuResult): void => {\n flowResult = result;\n };\n\n const handleCancel = (): void => {\n flowResult = {\n action: \"cancel\",\n output: \"\",\n cancelled: true,\n };\n };\n\n // Render the Ink app (single render for entire flow)\n const { waitUntilExit } = render(\n createElement(SearchFlowApp, {\n choices,\n filterFn,\n visibleCount: effectiveLimit,\n defaultSort,\n defaultKeyFormat: config.defaultKeyFormat ?? \"pandoc\",\n defaultStyle: config.defaultStyle ?? \"apa\",\n debounceMs: config.debounceMs,\n onComplete: handleComplete,\n onCancel: handleCancel,\n })\n );\n\n // Wait for the app to exit, then resolve\n waitUntilExit()\n .then(() => {\n restoreStdinAfterInk();\n resolve(flowResult);\n })\n .catch(() => {\n restoreStdinAfterInk();\n resolve({\n action: \"cancel\",\n output: \"\",\n cancelled: true,\n });\n });\n });\n}\n","/**\n * CiteFlowApp - Single App for cite command flow\n *\n * Implements the Single App Pattern (ADR-015) for the cite command.\n * Manages state transitions: reference selection → style selection → exiting\n */\n\nimport { Box, useApp } from \"ink\";\nimport type React from \"react\";\nimport { createElement, useEffect, useState } from \"react\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport { SearchableMultiSelect } from \"../components/SearchableMultiSelect.js\";\nimport { Select, type SelectOption } from \"../components/Select.js\";\nimport type { Choice, SortOption } from \"../components/index.js\";\n\n// Flow states\ntype FlowState = \"search\" | \"style\" | \"exiting\";\n\n/**\n * Result from the cite flow\n */\nexport interface CiteFlowResult {\n /** Selected reference IDs */\n identifiers: string[];\n /** Selected style (if style selection was shown) */\n style?: string;\n /** Whether the flow was cancelled */\n cancelled: boolean;\n}\n\n/**\n * Props for CiteFlowApp\n */\nexport interface CiteFlowAppProps {\n /** All reference choices */\n choices: Choice<CslItem>[];\n /** Filter function for search */\n filterFn: (query: string, choices: Choice<CslItem>[]) => Choice<CslItem>[];\n /** Number of visible items */\n visibleCount: number;\n /** Default sort option */\n defaultSort: SortOption;\n /** Style options for style selection */\n styleOptions: SelectOption<string>[];\n /** Whether to show style selection (false if style already specified) */\n showStyleSelect: boolean;\n /** Callback when flow completes */\n onComplete: (result: CiteFlowResult) => void;\n /** Callback when flow is cancelled */\n onCancel: () => void;\n}\n\n/**\n * CiteFlowApp component\n *\n * Single Ink app that handles the entire cite flow:\n * 1. Reference selection (SearchableMultiSelect)\n * 2. Style selection (Select) - optional\n * 3. Exit\n */\nexport function CiteFlowApp({\n choices,\n filterFn,\n visibleCount,\n defaultSort,\n styleOptions,\n showStyleSelect,\n onComplete,\n onCancel,\n}: CiteFlowAppProps): React.ReactElement {\n const { exit } = useApp();\n const [state, setState] = useState<FlowState>(\"search\");\n const [selectedItems, setSelectedItems] = useState<CslItem[]>([]);\n const [pendingResult, setPendingResult] = useState<CiteFlowResult | null>(null);\n\n // Exit when entering \"exiting\" state\n useEffect(() => {\n if (state === \"exiting\" && pendingResult) {\n exit();\n if (pendingResult.cancelled) {\n onCancel();\n } else {\n onComplete(pendingResult);\n }\n }\n }, [state, pendingResult, exit, onCancel, onComplete]);\n\n // Transition to exiting state with result\n const exitWith = (result: CiteFlowResult) => {\n setPendingResult(result);\n setState(\"exiting\");\n };\n\n // Handle search submission\n const handleSearchSubmit = (selected: Choice<CslItem>[]) => {\n if (selected.length === 0) {\n exitWith({ identifiers: [], cancelled: true });\n return;\n }\n const items = selected.map((c) => c.value);\n setSelectedItems(items);\n\n if (showStyleSelect) {\n setState(\"style\");\n } else {\n // No style selection needed, complete immediately\n exitWith({\n identifiers: items.map((item) => item.id),\n cancelled: false,\n });\n }\n };\n\n // Handle search cancel\n const handleSearchCancel = () => {\n exitWith({ identifiers: [], cancelled: true });\n };\n\n // Handle style selection\n const handleStyleSelect = (style: string) => {\n exitWith({\n identifiers: selectedItems.map((item) => item.id),\n style,\n cancelled: false,\n });\n };\n\n // Handle style cancel (go back to search)\n const handleStyleCancel = () => {\n setState(\"search\");\n };\n\n // Render based on current state\n if (state === \"exiting\") {\n return createElement(Box);\n }\n\n if (state === \"search\") {\n return createElement(SearchableMultiSelect<CslItem>, {\n choices,\n filterFn,\n visibleCount,\n onSubmit: handleSearchSubmit,\n onCancel: handleSearchCancel,\n header: \"Select references to cite\",\n placeholder: \"Type to search...\",\n defaultSort,\n });\n }\n\n // state === \"style\"\n const count = selectedItems.length;\n const refWord = count === 1 ? \"reference\" : \"references\";\n return createElement(Select<string>, {\n options: styleOptions,\n message: `Select citation style for ${count} ${refWord}:`,\n onSelect: handleStyleSelect,\n onCancel: handleStyleCancel,\n });\n}\n","/**\n * Runner for CiteFlowApp\n *\n * Provides the public API for running the cite flow.\n */\n\nimport { render } from \"ink\";\nimport { createElement } from \"react\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport type { SearchResult } from \"../../search/types.js\";\nimport { restoreStdinAfterInk } from \"../alternate-screen.js\";\nimport { formatIdentifiers } from \"../choice-builder.js\";\nimport {\n type Choice,\n type SelectOption,\n type SortOption,\n calculateEffectiveLimit,\n} from \"../components/index.js\";\nimport { formatAuthors } from \"../format.js\";\nimport { CiteFlowApp, type CiteFlowResult } from \"./CiteFlowApp.js\";\n\n/**\n * Configuration for the cite flow\n */\nexport interface CiteFlowConfig {\n /** Maximum number of results to display */\n limit: number;\n}\n\n/**\n * Search function type for filtering references\n */\nexport type SearchFunction = (query: string) => SearchResult[];\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) return undefined;\n const firstDatePart = dateParts[0];\n if (!firstDatePart || firstDatePart.length === 0) return undefined;\n return firstDatePart[0];\n}\n\n/**\n * Extract published date from CSL item\n */\nfunction extractPublishedDate(item: CslItem): Date | undefined {\n const dateParts = item.issued?.[\"date-parts\"];\n if (!dateParts || dateParts.length === 0) return undefined;\n const firstDatePart = dateParts[0];\n if (!firstDatePart || firstDatePart.length === 0) return undefined;\n const [year, month = 1, day = 1] = firstDatePart;\n if (year === undefined) return undefined;\n return new Date(year, month - 1, day);\n}\n\n/**\n * Extract updated date from CSL item (from custom.timestamp)\n */\nfunction extractUpdatedDate(item: CslItem): Date | undefined {\n const dateStr = item.custom?.timestamp;\n if (!dateStr || typeof dateStr !== \"string\") return undefined;\n const date = new Date(dateStr);\n return Number.isNaN(date.getTime()) ? undefined : date;\n}\n\n/**\n * Extract created date from CSL item (from custom.created_at)\n */\nfunction extractCreatedDate(item: CslItem): Date | undefined {\n const dateStr = item.custom?.created_at;\n if (!dateStr || typeof dateStr !== \"string\") return undefined;\n const date = new Date(dateStr);\n return Number.isNaN(date.getTime()) ? undefined : date;\n}\n\n/**\n * Format item type for display\n */\nfunction formatType(type: string): string {\n const typeMap: Record<string, string> = {\n \"article-journal\": \"Journal article\",\n \"article-magazine\": \"Magazine article\",\n \"article-newspaper\": \"Newspaper article\",\n book: \"Book\",\n chapter: \"Book chapter\",\n \"paper-conference\": \"Conference paper\",\n thesis: \"Thesis\",\n report: \"Report\",\n webpage: \"Web page\",\n };\n return typeMap[type] ?? type;\n}\n\n/**\n * Convert CslItem to Choice for SearchableMultiSelect\n */\nfunction toChoice(item: CslItem): Choice<CslItem> {\n const authors = formatAuthors(item.author);\n const year = extractYear(item);\n const identifiers = formatIdentifiers(item);\n const itemType = formatType(item.type);\n\n // Build meta line: Year · Type · Identifiers\n const metaParts: string[] = [];\n if (year) metaParts.push(String(year));\n metaParts.push(itemType);\n if (identifiers) metaParts.push(identifiers);\n\n const updatedDate = extractUpdatedDate(item);\n const createdDate = extractCreatedDate(item);\n const publishedDate = extractPublishedDate(item);\n\n return {\n id: item.id,\n title: item.title ?? \"(No title)\",\n subtitle: authors || \"(No authors)\",\n meta: metaParts.join(\" · \"),\n value: item,\n ...(updatedDate && { updatedDate }),\n ...(createdDate && { createdDate }),\n ...(publishedDate && { publishedDate }),\n };\n}\n\n/**\n * Options for running the cite flow\n */\nexport interface RunCiteFlowOptions {\n /** All references available for selection */\n allReferences: CslItem[];\n /** Search function for filtering */\n searchFn: SearchFunction;\n /** Flow configuration */\n config: CiteFlowConfig;\n /** Style options for style selection */\n styleOptions: SelectOption<string>[];\n /** Whether to show style selection */\n showStyleSelect: boolean;\n}\n\n/**\n * Run the cite flow (reference selection → style selection if needed)\n *\n * This is the main entry point for interactive cite command.\n */\nexport async function runCiteFlow(options: RunCiteFlowOptions): Promise<CiteFlowResult> {\n const { allReferences, searchFn, config, styleOptions, showStyleSelect } = options;\n\n // Convert references to choices\n const choices = allReferences.map(toChoice);\n\n // Calculate effective visible count\n const effectiveLimit = calculateEffectiveLimit(config.limit);\n\n // Create filter function using the provided search function\n const filterFn = (query: string, choices: Choice<CslItem>[]): Choice<CslItem>[] => {\n if (!query.trim()) return choices;\n\n const results = searchFn(query);\n return results.map((r) => toChoice(r.reference));\n };\n\n // Default sort option\n const defaultSort: SortOption = \"updated-desc\";\n\n // Create a promise to capture the result\n return new Promise<CiteFlowResult>((resolve) => {\n let flowResult: CiteFlowResult = {\n identifiers: [],\n cancelled: true,\n };\n\n const handleComplete = (result: CiteFlowResult): void => {\n flowResult = result;\n };\n\n const handleCancel = (): void => {\n flowResult = {\n identifiers: [],\n cancelled: true,\n };\n };\n\n // Render the Ink app (single render for entire flow)\n const { waitUntilExit } = render(\n createElement(CiteFlowApp, {\n choices,\n filterFn,\n visibleCount: effectiveLimit,\n defaultSort,\n styleOptions,\n showStyleSelect,\n onComplete: handleComplete,\n onCancel: handleCancel,\n })\n );\n\n // Wait for the app to exit, then resolve\n waitUntilExit()\n .then(() => {\n restoreStdinAfterInk();\n resolve(flowResult);\n })\n .catch(() => {\n restoreStdinAfterInk();\n resolve({\n identifiers: [],\n cancelled: true,\n });\n });\n });\n}\n"],"names":["toChoice","choices"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-BuQm8A5F.js","sources":["../../src/features/interactive/apps/SearchFlowApp.tsx","../../src/features/interactive/apps/runSearchFlow.ts","../../src/features/interactive/apps/CiteFlowApp.tsx","../../src/features/interactive/apps/runCiteFlow.ts"],"sourcesContent":["/**\n * SearchFlowApp - Single App for search -t flow\n *\n * Manages state transitions: search → action → (style/output-format if needed)\n * Following React Ink Single App Pattern (ADR-015)\n */\n\nimport { Box, useApp } from \"ink\";\nimport type React from \"react\";\nimport { createElement, useEffect, useState } from \"react\";\nimport type { CitationKeyFormat } from \"../../../config/schema.js\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport {\n type ActionMenuResult,\n type ActionType,\n OUTPUT_FORMAT_CHOICES,\n type OutputFormatType,\n STYLE_CHOICES,\n generateOutput,\n getActionChoices,\n isSideEffectAction,\n} from \"../action-menu.js\";\nimport {\n type Choice,\n SearchableMultiSelect,\n Select,\n type SortOption,\n} from \"../components/index.js\";\n\n/**\n * Flow states for the search flow\n */\ntype FlowState = \"search\" | \"action\" | \"style\" | \"output-format\" | \"exiting\";\n\n/**\n * Props for SearchFlowApp\n */\nexport interface SearchFlowAppProps {\n /** Choices for the search prompt */\n choices: Choice<CslItem>[];\n /** Filter function for search */\n filterFn: (query: string, choices: Choice<CslItem>[]) => Choice<CslItem>[];\n /** Number of visible items */\n visibleCount: number;\n /** Default sort option */\n defaultSort: SortOption;\n /** Default citation key format */\n defaultKeyFormat: CitationKeyFormat;\n /** Default citation style */\n defaultStyle: string;\n /** Debounce delay in milliseconds for search filtering */\n debounceMs?: number;\n /** Callback when flow completes */\n onComplete: (result: ActionMenuResult) => void;\n /** Callback when flow is cancelled */\n onCancel: () => void;\n}\n\n/**\n * SearchFlowApp component\n *\n * Single App that manages search → action → style/output-format flow\n */\nexport function SearchFlowApp({\n choices,\n filterFn,\n visibleCount,\n defaultSort,\n defaultKeyFormat,\n defaultStyle,\n debounceMs,\n onComplete,\n onCancel,\n}: SearchFlowAppProps): React.ReactElement {\n const { exit } = useApp();\n const [state, setState] = useState<FlowState>(\"search\");\n const [selectedItems, setSelectedItems] = useState<CslItem[]>([]);\n const [pendingResult, setPendingResult] = useState<ActionMenuResult | null>(null);\n\n // Exit when entering \"exiting\" state (after rendering empty component)\n useEffect(() => {\n if (state === \"exiting\" && pendingResult) {\n exit();\n if (pendingResult.cancelled) {\n onCancel();\n } else {\n onComplete(pendingResult);\n }\n }\n }, [state, pendingResult, exit, onCancel, onComplete]);\n\n // Transition to exiting state with result\n const exitWith = (result: ActionMenuResult) => {\n setPendingResult(result);\n setState(\"exiting\");\n };\n\n // Handle search submission\n const handleSearchSubmit = (selected: Choice<CslItem>[]) => {\n if (selected.length === 0) {\n exitWith({ action: \"cancel\", output: \"\", cancelled: true });\n return;\n }\n setSelectedItems(selected.map((c) => c.value));\n setState(\"action\");\n };\n\n // Handle search cancel\n const handleSearchCancel = () => {\n exitWith({ action: \"cancel\", output: \"\", cancelled: true });\n };\n\n // Handle action selection\n const handleActionSelect = (action: ActionType) => {\n if (action === \"cancel\") {\n exitWith({ action: \"cancel\", output: \"\", cancelled: true });\n return;\n }\n\n // If cite-choose, go to style selection\n if (action === \"cite-choose\") {\n setState(\"style\");\n return;\n }\n\n // If output-format, go to output format submenu\n if (action === \"output-format\") {\n setState(\"output-format\");\n return;\n }\n\n // Handle side-effect actions\n if (isSideEffectAction(action)) {\n exitWith({ action, output: \"\", cancelled: false, selectedItems });\n return;\n }\n\n // Generate output and complete\n const output = generateOutput(action, selectedItems, {\n defaultKeyFormat,\n defaultStyle,\n });\n exitWith({ action, output, cancelled: false });\n };\n\n // Handle action cancel (go back to search)\n const handleActionCancel = () => {\n setState(\"search\");\n };\n\n // Handle style selection\n const handleStyleSelect = (style: string) => {\n const output = generateOutput(\"cite-choose\", selectedItems, {\n defaultKeyFormat,\n defaultStyle: style,\n });\n exitWith({ action: \"cite-choose\", output, cancelled: false });\n };\n\n // Handle style cancel (go back to action)\n const handleStyleCancel = () => {\n setState(\"action\");\n };\n\n // Handle output format selection\n const handleOutputFormatSelect = (format: OutputFormatType) => {\n if (format === \"cancel\") {\n setState(\"action\");\n return;\n }\n\n const output = generateOutput(format, selectedItems, {\n defaultKeyFormat,\n defaultStyle,\n });\n exitWith({ action: \"output-format\", output, cancelled: false });\n };\n\n // Handle output format cancel (go back to action)\n const handleOutputFormatCancel = () => {\n setState(\"action\");\n };\n\n // Render based on current state\n if (state === \"exiting\") {\n // Empty component - Ink will clear the previous content\n return createElement(Box);\n }\n\n if (state === \"search\") {\n return createElement(SearchableMultiSelect<CslItem>, {\n choices,\n filterFn,\n visibleCount,\n onSubmit: handleSearchSubmit,\n onCancel: handleSearchCancel,\n header: \"Search references\",\n placeholder: \"Type to search...\",\n defaultSort,\n ...(debounceMs !== undefined && { debounceMs }),\n });\n }\n\n if (state === \"action\") {\n const count = selectedItems.length;\n const refWord = count === 1 ? \"reference\" : \"references\";\n return createElement(Select<ActionType>, {\n key: \"action\",\n options: getActionChoices(count, { defaultKeyFormat }),\n message: `Action for ${count} selected ${refWord}:`,\n onSelect: handleActionSelect,\n onCancel: handleActionCancel,\n });\n }\n\n if (state === \"output-format\") {\n return createElement(Select<OutputFormatType>, {\n key: \"output-format\",\n options: OUTPUT_FORMAT_CHOICES,\n message: \"Select output format:\",\n onSelect: handleOutputFormatSelect,\n onCancel: handleOutputFormatCancel,\n });\n }\n\n // state === \"style\"\n return createElement(Select<string>, {\n key: \"style\",\n options: STYLE_CHOICES,\n message: \"Select citation style:\",\n onSelect: handleStyleSelect,\n onCancel: handleStyleCancel,\n });\n}\n","/**\n * Runner for SearchFlowApp\n *\n * Provides the public API for running the search flow.\n */\n\nimport { render } from \"ink\";\nimport { createElement } from \"react\";\nimport type { CitationKeyFormat } from \"../../../config/schema.js\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport type { SearchResult } from \"../../search/types.js\";\nimport type { ActionMenuResult } from \"../action-menu.js\";\nimport { restoreStdinAfterInk } from \"../alternate-screen.js\";\nimport { toChoice } from \"../choice-builder.js\";\nimport { type Choice, type SortOption, calculateEffectiveLimit } from \"../components/index.js\";\nimport { SearchFlowApp } from \"./SearchFlowApp.js\";\n\n/**\n * Configuration for the search flow\n */\nexport interface SearchFlowConfig {\n /** Maximum number of results to display */\n limit: number;\n /** Debounce delay in milliseconds for search filtering */\n debounceMs: number;\n /** Default citation key format */\n defaultKeyFormat?: CitationKeyFormat;\n /** Default citation style */\n defaultStyle?: string;\n}\n\n/**\n * Search function type for filtering references\n */\nexport type SearchFunction = (query: string) => SearchResult[];\n\n/**\n * Run the search flow (search → action → style if needed)\n *\n * This is the main entry point for the `search -t` command.\n */\nexport async function runSearchFlow(\n allReferences: CslItem[],\n searchFn: SearchFunction,\n config: SearchFlowConfig\n): Promise<ActionMenuResult> {\n // Convert references to choices and build lookup map\n const choices = allReferences.map(toChoice);\n const choiceMap = new Map(choices.map((c) => [c.id, c]));\n\n // Calculate effective visible count\n const effectiveLimit = calculateEffectiveLimit(config.limit);\n\n // Create filter function using the provided search function\n const filterFn = (query: string, choices: Choice<CslItem>[]): Choice<CslItem>[] => {\n if (!query.trim()) return choices;\n\n const results = searchFn(query);\n return results.flatMap((r) => {\n const choice = choiceMap.get(r.reference.id);\n return choice ? [choice] : [];\n });\n };\n\n // Default sort option\n const defaultSort: SortOption = \"updated-desc\";\n\n // Create a promise to capture the result\n return new Promise<ActionMenuResult>((resolve) => {\n let flowResult: ActionMenuResult = {\n action: \"cancel\",\n output: \"\",\n cancelled: true,\n };\n\n const handleComplete = (result: ActionMenuResult): void => {\n flowResult = result;\n };\n\n const handleCancel = (): void => {\n flowResult = {\n action: \"cancel\",\n output: \"\",\n cancelled: true,\n };\n };\n\n // Render the Ink app (single render for entire flow)\n const { waitUntilExit } = render(\n createElement(SearchFlowApp, {\n choices,\n filterFn,\n visibleCount: effectiveLimit,\n defaultSort,\n defaultKeyFormat: config.defaultKeyFormat ?? \"pandoc\",\n defaultStyle: config.defaultStyle ?? \"apa\",\n debounceMs: config.debounceMs,\n onComplete: handleComplete,\n onCancel: handleCancel,\n })\n );\n\n // Wait for the app to exit, then resolve\n waitUntilExit()\n .then(() => {\n restoreStdinAfterInk();\n resolve(flowResult);\n })\n .catch(() => {\n restoreStdinAfterInk();\n resolve({\n action: \"cancel\",\n output: \"\",\n cancelled: true,\n });\n });\n });\n}\n","/**\n * CiteFlowApp - Single App for cite command flow\n *\n * Implements the Single App Pattern (ADR-015) for the cite command.\n * Manages state transitions: reference selection → style selection → exiting\n */\n\nimport { Box, useApp } from \"ink\";\nimport type React from \"react\";\nimport { createElement, useEffect, useState } from \"react\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport { SearchableMultiSelect } from \"../components/SearchableMultiSelect.js\";\nimport { Select, type SelectOption } from \"../components/Select.js\";\nimport type { Choice, SortOption } from \"../components/index.js\";\n\n// Flow states\ntype FlowState = \"search\" | \"style\" | \"exiting\";\n\n/**\n * Result from the cite flow\n */\nexport interface CiteFlowResult {\n /** Selected reference IDs */\n identifiers: string[];\n /** Selected style (if style selection was shown) */\n style?: string;\n /** Whether the flow was cancelled */\n cancelled: boolean;\n}\n\n/**\n * Props for CiteFlowApp\n */\nexport interface CiteFlowAppProps {\n /** All reference choices */\n choices: Choice<CslItem>[];\n /** Filter function for search */\n filterFn: (query: string, choices: Choice<CslItem>[]) => Choice<CslItem>[];\n /** Number of visible items */\n visibleCount: number;\n /** Default sort option */\n defaultSort: SortOption;\n /** Style options for style selection */\n styleOptions: SelectOption<string>[];\n /** Whether to show style selection (false if style already specified) */\n showStyleSelect: boolean;\n /** Callback when flow completes */\n onComplete: (result: CiteFlowResult) => void;\n /** Callback when flow is cancelled */\n onCancel: () => void;\n}\n\n/**\n * CiteFlowApp component\n *\n * Single Ink app that handles the entire cite flow:\n * 1. Reference selection (SearchableMultiSelect)\n * 2. Style selection (Select) - optional\n * 3. Exit\n */\nexport function CiteFlowApp({\n choices,\n filterFn,\n visibleCount,\n defaultSort,\n styleOptions,\n showStyleSelect,\n onComplete,\n onCancel,\n}: CiteFlowAppProps): React.ReactElement {\n const { exit } = useApp();\n const [state, setState] = useState<FlowState>(\"search\");\n const [selectedItems, setSelectedItems] = useState<CslItem[]>([]);\n const [pendingResult, setPendingResult] = useState<CiteFlowResult | null>(null);\n\n // Exit when entering \"exiting\" state\n useEffect(() => {\n if (state === \"exiting\" && pendingResult) {\n exit();\n if (pendingResult.cancelled) {\n onCancel();\n } else {\n onComplete(pendingResult);\n }\n }\n }, [state, pendingResult, exit, onCancel, onComplete]);\n\n // Transition to exiting state with result\n const exitWith = (result: CiteFlowResult) => {\n setPendingResult(result);\n setState(\"exiting\");\n };\n\n // Handle search submission\n const handleSearchSubmit = (selected: Choice<CslItem>[]) => {\n if (selected.length === 0) {\n exitWith({ identifiers: [], cancelled: true });\n return;\n }\n const items = selected.map((c) => c.value);\n setSelectedItems(items);\n\n if (showStyleSelect) {\n setState(\"style\");\n } else {\n // No style selection needed, complete immediately\n exitWith({\n identifiers: items.map((item) => item.id),\n cancelled: false,\n });\n }\n };\n\n // Handle search cancel\n const handleSearchCancel = () => {\n exitWith({ identifiers: [], cancelled: true });\n };\n\n // Handle style selection\n const handleStyleSelect = (style: string) => {\n exitWith({\n identifiers: selectedItems.map((item) => item.id),\n style,\n cancelled: false,\n });\n };\n\n // Handle style cancel (go back to search)\n const handleStyleCancel = () => {\n setState(\"search\");\n };\n\n // Render based on current state\n if (state === \"exiting\") {\n return createElement(Box);\n }\n\n if (state === \"search\") {\n return createElement(SearchableMultiSelect<CslItem>, {\n choices,\n filterFn,\n visibleCount,\n onSubmit: handleSearchSubmit,\n onCancel: handleSearchCancel,\n header: \"Select references to cite\",\n placeholder: \"Type to search...\",\n defaultSort,\n });\n }\n\n // state === \"style\"\n const count = selectedItems.length;\n const refWord = count === 1 ? \"reference\" : \"references\";\n return createElement(Select<string>, {\n options: styleOptions,\n message: `Select citation style for ${count} ${refWord}:`,\n onSelect: handleStyleSelect,\n onCancel: handleStyleCancel,\n });\n}\n","/**\n * Runner for CiteFlowApp\n *\n * Provides the public API for running the cite flow.\n */\n\nimport { render } from \"ink\";\nimport { createElement } from \"react\";\nimport type { CslItem } from \"../../../core/csl-json/types.js\";\nimport type { SearchResult } from \"../../search/types.js\";\nimport { restoreStdinAfterInk } from \"../alternate-screen.js\";\nimport { formatIdentifiers } from \"../choice-builder.js\";\nimport {\n type Choice,\n type SelectOption,\n type SortOption,\n calculateEffectiveLimit,\n} from \"../components/index.js\";\nimport { formatAuthors } from \"../format.js\";\nimport { CiteFlowApp, type CiteFlowResult } from \"./CiteFlowApp.js\";\n\n/**\n * Configuration for the cite flow\n */\nexport interface CiteFlowConfig {\n /** Maximum number of results to display */\n limit: number;\n}\n\n/**\n * Search function type for filtering references\n */\nexport type SearchFunction = (query: string) => SearchResult[];\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) return undefined;\n const firstDatePart = dateParts[0];\n if (!firstDatePart || firstDatePart.length === 0) return undefined;\n return firstDatePart[0];\n}\n\n/**\n * Extract published date from CSL item\n */\nfunction extractPublishedDate(item: CslItem): Date | undefined {\n const dateParts = item.issued?.[\"date-parts\"];\n if (!dateParts || dateParts.length === 0) return undefined;\n const firstDatePart = dateParts[0];\n if (!firstDatePart || firstDatePart.length === 0) return undefined;\n const [year, month = 1, day = 1] = firstDatePart;\n if (year === undefined) return undefined;\n return new Date(year, month - 1, day);\n}\n\n/**\n * Extract updated date from CSL item (from custom.timestamp)\n */\nfunction extractUpdatedDate(item: CslItem): Date | undefined {\n const dateStr = item.custom?.timestamp;\n if (!dateStr || typeof dateStr !== \"string\") return undefined;\n const date = new Date(dateStr);\n return Number.isNaN(date.getTime()) ? undefined : date;\n}\n\n/**\n * Extract created date from CSL item (from custom.created_at)\n */\nfunction extractCreatedDate(item: CslItem): Date | undefined {\n const dateStr = item.custom?.created_at;\n if (!dateStr || typeof dateStr !== \"string\") return undefined;\n const date = new Date(dateStr);\n return Number.isNaN(date.getTime()) ? undefined : date;\n}\n\n/**\n * Format item type for display\n */\nfunction formatType(type: string): string {\n const typeMap: Record<string, string> = {\n \"article-journal\": \"Journal article\",\n \"article-magazine\": \"Magazine article\",\n \"article-newspaper\": \"Newspaper article\",\n book: \"Book\",\n chapter: \"Book chapter\",\n \"paper-conference\": \"Conference paper\",\n thesis: \"Thesis\",\n report: \"Report\",\n webpage: \"Web page\",\n };\n return typeMap[type] ?? type;\n}\n\n/**\n * Convert CslItem to Choice for SearchableMultiSelect\n */\nfunction toChoice(item: CslItem): Choice<CslItem> {\n const authors = formatAuthors(item.author);\n const year = extractYear(item);\n const identifiers = formatIdentifiers(item);\n const itemType = formatType(item.type);\n\n // Build meta line: Year · Type · Identifiers\n const metaParts: string[] = [];\n if (year) metaParts.push(String(year));\n metaParts.push(itemType);\n if (identifiers) metaParts.push(identifiers);\n\n const updatedDate = extractUpdatedDate(item);\n const createdDate = extractCreatedDate(item);\n const publishedDate = extractPublishedDate(item);\n\n return {\n id: item.id,\n title: item.title ?? \"(No title)\",\n subtitle: authors || \"(No authors)\",\n meta: metaParts.join(\" · \"),\n value: item,\n ...(updatedDate && { updatedDate }),\n ...(createdDate && { createdDate }),\n ...(publishedDate && { publishedDate }),\n };\n}\n\n/**\n * Options for running the cite flow\n */\nexport interface RunCiteFlowOptions {\n /** All references available for selection */\n allReferences: CslItem[];\n /** Search function for filtering */\n searchFn: SearchFunction;\n /** Flow configuration */\n config: CiteFlowConfig;\n /** Style options for style selection */\n styleOptions: SelectOption<string>[];\n /** Whether to show style selection */\n showStyleSelect: boolean;\n}\n\n/**\n * Run the cite flow (reference selection → style selection if needed)\n *\n * This is the main entry point for interactive cite command.\n */\nexport async function runCiteFlow(options: RunCiteFlowOptions): Promise<CiteFlowResult> {\n const { allReferences, searchFn, config, styleOptions, showStyleSelect } = options;\n\n // Convert references to choices\n const choices = allReferences.map(toChoice);\n\n // Calculate effective visible count\n const effectiveLimit = calculateEffectiveLimit(config.limit);\n\n // Create filter function using the provided search function\n const filterFn = (query: string, choices: Choice<CslItem>[]): Choice<CslItem>[] => {\n if (!query.trim()) return choices;\n\n const results = searchFn(query);\n return results.map((r) => toChoice(r.reference));\n };\n\n // Default sort option\n const defaultSort: SortOption = \"updated-desc\";\n\n // Create a promise to capture the result\n return new Promise<CiteFlowResult>((resolve) => {\n let flowResult: CiteFlowResult = {\n identifiers: [],\n cancelled: true,\n };\n\n const handleComplete = (result: CiteFlowResult): void => {\n flowResult = result;\n };\n\n const handleCancel = (): void => {\n flowResult = {\n identifiers: [],\n cancelled: true,\n };\n };\n\n // Render the Ink app (single render for entire flow)\n const { waitUntilExit } = render(\n createElement(CiteFlowApp, {\n choices,\n filterFn,\n visibleCount: effectiveLimit,\n defaultSort,\n styleOptions,\n showStyleSelect,\n onComplete: handleComplete,\n onCancel: handleCancel,\n })\n );\n\n // Wait for the app to exit, then resolve\n waitUntilExit()\n .then(() => {\n restoreStdinAfterInk();\n resolve(flowResult);\n })\n .catch(() => {\n restoreStdinAfterInk();\n resolve({\n identifiers: [],\n cancelled: true,\n });\n });\n });\n}\n"],"names":["toChoice","choices"],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Command, Option } from "commander";
|
|
2
|
-
import { n as normalizePathForOutput, d as deleteDirectoryIfEmpty, p as parseFilename, i as isReservedRole, e as ensureDirectory, a as addAttachment, R as RESERVED_ROLES, b as generateFilename, c as findFulltextFiles, h as findFulltextFile, j as extensionToFormat, k as fulltextAttach, l as fulltextDiscover, m as fulltextFetch, o as fulltextConvert, q as fulltextGet, r as getExtension, s as getDefaultExportFromCjs, B as BUILTIN_STYLES, t as packageJson, u as getFulltextAttachmentTypes, v as startServerWithFileWatcher, w as BUILTIN_CONVERTER_NAMES, x as BUILTIN_CONVERTER_INFO } from "./index-
|
|
2
|
+
import { n as normalizePathForOutput, d as deleteDirectoryIfEmpty, p as parseFilename, i as isReservedRole, e as ensureDirectory, a as addAttachment, R as RESERVED_ROLES, b as generateFilename, c as findFulltextFiles, h as findFulltextFile, j as extensionToFormat, k as fulltextAttach, l as fulltextDiscover, m as fulltextFetch, o as fulltextConvert, q as fulltextGet, r as getExtension, s as getDefaultExportFromCjs, B as BUILTIN_STYLES, t as packageJson, u as getFulltextAttachmentTypes, v as startServerWithFileWatcher, w as BUILTIN_CONVERTER_NAMES, x as BUILTIN_CONVERTER_INFO } from "./index-B4-i4PrU.js";
|
|
3
3
|
import { i as isEqual, M as MANAGED_CUSTOM_FIELDS, w as writeFileAtomic, L as Library, h as CslItemSchema, p as pickDefined, a as sortOrderSchema, z as paginationOptionsSchema, F as FileWatcher, b as sortFieldSchema, y as searchSortFieldSchema } from "./file-watcher-CWHg1yol.js";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
|
-
import { promises, readFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { promises, readFileSync, existsSync, mkdirSync, writeFileSync, lstatSync, unlinkSync, rmSync, symlinkSync } from "node:fs";
|
|
6
6
|
import * as os from "node:os";
|
|
7
|
+
import { homedir } from "node:os";
|
|
7
8
|
import * as path from "node:path";
|
|
8
|
-
import path__default, { join, basename, dirname } from "node:path";
|
|
9
|
+
import path__default, { join, basename, dirname, relative } from "node:path";
|
|
9
10
|
import { readFile, unlink, stat, readdir, rename } from "node:fs/promises";
|
|
10
11
|
import { o as openWithSystemApp, l as loadConfig, e as getDefaultCurrentDirConfigFilename, h as getDefaultUserConfigPath } from "./loader-BG2eomDC.js";
|
|
11
12
|
import { spawn, spawnSync } from "node:child_process";
|
|
@@ -893,15 +894,15 @@ class OperationsLibrary {
|
|
|
893
894
|
}
|
|
894
895
|
// High-level operations
|
|
895
896
|
async search(options) {
|
|
896
|
-
const { searchReferences } = await import("./index-
|
|
897
|
+
const { searchReferences } = await import("./index-B4-i4PrU.js").then((n) => n.H);
|
|
897
898
|
return searchReferences(this.library, options);
|
|
898
899
|
}
|
|
899
900
|
async list(options) {
|
|
900
|
-
const { listReferences } = await import("./index-
|
|
901
|
+
const { listReferences } = await import("./index-B4-i4PrU.js").then((n) => n.G);
|
|
901
902
|
return listReferences(this.library, options ?? {});
|
|
902
903
|
}
|
|
903
904
|
async cite(options) {
|
|
904
|
-
const { citeReferences } = await import("./index-
|
|
905
|
+
const { citeReferences } = await import("./index-B4-i4PrU.js").then((n) => n.F);
|
|
905
906
|
const defaultStyle = options.defaultStyle ?? this.citationConfig?.defaultStyle;
|
|
906
907
|
const cslDirectory = options.cslDirectory ?? this.citationConfig?.cslDirectory;
|
|
907
908
|
const mergedOptions = {
|
|
@@ -912,36 +913,36 @@ class OperationsLibrary {
|
|
|
912
913
|
return citeReferences(this.library, mergedOptions);
|
|
913
914
|
}
|
|
914
915
|
async import(inputs, options) {
|
|
915
|
-
const { addReferences } = await import("./index-
|
|
916
|
+
const { addReferences } = await import("./index-B4-i4PrU.js").then((n) => n.D);
|
|
916
917
|
return addReferences(inputs, this.library, options ?? {});
|
|
917
918
|
}
|
|
918
919
|
async check(options) {
|
|
919
|
-
const { checkReferences } = await import("./index-
|
|
920
|
+
const { checkReferences } = await import("./index-B4-i4PrU.js").then((n) => n.E);
|
|
920
921
|
return checkReferences(this.library, options);
|
|
921
922
|
}
|
|
922
923
|
// Attachment operations
|
|
923
924
|
async attachAdd(options) {
|
|
924
|
-
const { addAttachment: addAttachment2 } = await import("./index-
|
|
925
|
+
const { addAttachment: addAttachment2 } = await import("./index-B8iEozpf.js");
|
|
925
926
|
return addAttachment2(this.library, options);
|
|
926
927
|
}
|
|
927
928
|
async attachList(options) {
|
|
928
|
-
const { listAttachments: listAttachments2 } = await import("./index-
|
|
929
|
+
const { listAttachments: listAttachments2 } = await import("./index-B8iEozpf.js");
|
|
929
930
|
return listAttachments2(this.library, options);
|
|
930
931
|
}
|
|
931
932
|
async attachGet(options) {
|
|
932
|
-
const { getAttachment: getAttachment2 } = await import("./index-
|
|
933
|
+
const { getAttachment: getAttachment2 } = await import("./index-B8iEozpf.js");
|
|
933
934
|
return getAttachment2(this.library, options);
|
|
934
935
|
}
|
|
935
936
|
async attachDetach(options) {
|
|
936
|
-
const { detachAttachment: detachAttachment2 } = await import("./index-
|
|
937
|
+
const { detachAttachment: detachAttachment2 } = await import("./index-B8iEozpf.js");
|
|
937
938
|
return detachAttachment2(this.library, options);
|
|
938
939
|
}
|
|
939
940
|
async attachSync(options) {
|
|
940
|
-
const { syncAttachments: syncAttachments2 } = await import("./index-
|
|
941
|
+
const { syncAttachments: syncAttachments2 } = await import("./index-B8iEozpf.js");
|
|
941
942
|
return syncAttachments2(this.library, options);
|
|
942
943
|
}
|
|
943
944
|
async attachOpen(options) {
|
|
944
|
-
const { openAttachment: openAttachment2 } = await import("./index-
|
|
945
|
+
const { openAttachment: openAttachment2 } = await import("./index-B8iEozpf.js");
|
|
945
946
|
return openAttachment2(this.library, options);
|
|
946
947
|
}
|
|
947
948
|
}
|
|
@@ -1806,7 +1807,7 @@ function getAttachExitCode(result) {
|
|
|
1806
1807
|
}
|
|
1807
1808
|
async function executeInteractiveSelect$3(context, config2) {
|
|
1808
1809
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
1809
|
-
const { selectReferencesOrExit } = await import("./reference-select-
|
|
1810
|
+
const { selectReferencesOrExit } = await import("./reference-select-B9b9Ez7Q.js");
|
|
1810
1811
|
const allReferences = await context.library.getAll();
|
|
1811
1812
|
const identifiers = await withAlternateScreen2(
|
|
1812
1813
|
() => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
|
|
@@ -2471,7 +2472,7 @@ async function handleCheckAction(identifiers, options, globalOpts) {
|
|
|
2471
2472
|
const jsonOptions = buildJsonOptionsFromRefs(options, outputFormat, result, allRefs);
|
|
2472
2473
|
outputCheckResult(result, outputFormat, jsonOptions);
|
|
2473
2474
|
if (options.fix && result.summary.warnings > 0 && allRefs) {
|
|
2474
|
-
const { runFixInteraction } = await import("./fix-interaction-
|
|
2475
|
+
const { runFixInteraction } = await import("./fix-interaction-DWUzp9Ri.js");
|
|
2475
2476
|
const findItem = (id2) => allRefs.find((item) => item.id === id2);
|
|
2476
2477
|
const fixResult = await runFixInteraction(result.results, context.library, findItem);
|
|
2477
2478
|
const removedSuffix = fixResult.removed.length > 0 ? `, ${fixResult.removed.length} removed` : "";
|
|
@@ -2539,7 +2540,7 @@ function outputCheckError(error, format2) {
|
|
|
2539
2540
|
}
|
|
2540
2541
|
async function selectReferencesInteractively(context, config2) {
|
|
2541
2542
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
2542
|
-
const { selectReferenceItemsOrExit } = await import("./reference-select-
|
|
2543
|
+
const { selectReferenceItemsOrExit } = await import("./reference-select-B9b9Ez7Q.js");
|
|
2543
2544
|
const allReferences = await context.library.getAll();
|
|
2544
2545
|
if (allReferences.length === 0) {
|
|
2545
2546
|
process.stderr.write("No references in library.\n");
|
|
@@ -2607,8 +2608,8 @@ function getCiteExitCode(result) {
|
|
|
2607
2608
|
}
|
|
2608
2609
|
async function executeInteractiveCite(options, context, config2) {
|
|
2609
2610
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
2610
|
-
const { runCiteFlow } = await import("./index-
|
|
2611
|
-
const { buildStyleChoices, listCustomStyles } = await import("./style-select-
|
|
2611
|
+
const { runCiteFlow } = await import("./index-BuQm8A5F.js");
|
|
2612
|
+
const { buildStyleChoices, listCustomStyles } = await import("./style-select-aXByJLOo.js");
|
|
2612
2613
|
const { search } = await import("./file-watcher-CWHg1yol.js").then((n) => n.B);
|
|
2613
2614
|
const { tokenize } = await import("./file-watcher-CWHg1yol.js").then((n) => n.A);
|
|
2614
2615
|
const { checkTTY } = await import("./tty-BMyaEOhX.js");
|
|
@@ -7291,7 +7292,7 @@ function formatEditOutput(result) {
|
|
|
7291
7292
|
}
|
|
7292
7293
|
async function executeInteractiveEdit(options, context, config2) {
|
|
7293
7294
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
7294
|
-
const { selectReferencesOrExit } = await import("./reference-select-
|
|
7295
|
+
const { selectReferencesOrExit } = await import("./reference-select-B9b9Ez7Q.js");
|
|
7295
7296
|
const allReferences = await context.library.getAll();
|
|
7296
7297
|
const identifiers = await withAlternateScreen2(
|
|
7297
7298
|
() => selectReferencesOrExit(allReferences, { multiSelect: true }, config2.cli.tui)
|
|
@@ -10867,7 +10868,7 @@ function getFulltextExitCode(result) {
|
|
|
10867
10868
|
}
|
|
10868
10869
|
async function executeInteractiveSelect$2(context, config2, multiSelect = false) {
|
|
10869
10870
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
10870
|
-
const { selectReferencesOrExit } = await import("./reference-select-
|
|
10871
|
+
const { selectReferencesOrExit } = await import("./reference-select-B9b9Ez7Q.js");
|
|
10871
10872
|
const allReferences = await context.library.getAll();
|
|
10872
10873
|
const identifiers = await withAlternateScreen2(
|
|
10873
10874
|
() => selectReferencesOrExit(allReferences, { multiSelect }, config2.cli.tui)
|
|
@@ -11290,6 +11291,79 @@ const fulltext = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
|
|
|
11290
11291
|
handleFulltextGetAction,
|
|
11291
11292
|
handleFulltextOpenAction
|
|
11292
11293
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
11294
|
+
const skillMd = '---\nname: ref\ndescription: Manage academic references in CSL-JSON format. Add papers by DOI/PMID/ISBN/arXiv, search and list references, generate citations, manage full-text PDFs, and check for retractions.\nallowed-tools:\n - Bash\n - Read\n - Write\n - Edit\n - Glob\n - Grep\n---\n\n# ref — Reference Manager\n\nA CLI tool for managing academic references using CSL-JSON as the single source of truth.\n\n## Quick Reference\n\n| Task | Command |\n|------|---------|\n| Add by DOI | `ref add 10.1234/example` |\n| Add by PMID | `ref add 12345678` |\n| Add by ISBN | `ref add 978-0-123456-78-9` |\n| Add by arXiv | `ref add 2301.12345` |\n| Add from BibTeX | `ref add references.bib` |\n| Add from RIS | `ref add references.ris` |\n| Search | `ref search "query"` |\n| List all | `ref list` |\n| Show details | `ref show <id>` |\n| Generate citation | `ref cite <id>` |\n| Export | `ref export <id> --output bibtex` |\n| Attach full-text | `ref fulltext attach <id> paper.pdf` |\n| Fetch full-text | `ref fulltext fetch <id>` |\n| Check retractions | `ref check <id>` |\n| Remove | `ref remove <id>` |\n| Update fields | `ref update <id> --set "title=New Title"` |\n| Configure | `ref config set <key> <value>` |\n\n## Search Syntax\n\n- Simple text: `ref search "machine learning"`\n- By author: `ref search "author:Smith"`\n- By year: `ref search "year:2024"`\n- By type: `ref search "type:article-journal"`\n- By tag: `ref search "tag:review"`\n- Combined: `ref search "author:Smith year:2024"`\n- Case-sensitive: prefix with `C/` (e.g., `ref search "C/RNA"`)\n\n## Output Formats\n\nMost commands support `--output` or `--json` flags:\n\n- `--json` — JSON output for programmatic use\n- `--output bibtex` — BibTeX format\n- `--output yaml` — YAML format\n\n## Full Help\n\nRun `ref --help` for all commands and options.\nRun `ref <command> --help` for command-specific help.\n';
|
|
11295
|
+
const fulltextMd = '---\nname: ref/fulltext\ndescription: Guide for managing full-text PDFs and Markdown files. Covers fetching, attaching, converting, and organizing full-text content.\n---\n\n# Full-Text Management\n\nGuide for working with full-text files (PDFs and Markdown) using `ref`.\n\n## Attach Files\n\n```bash\n# Attach a PDF\nref fulltext attach <id> paper.pdf\n\n# Attach Markdown\nref fulltext attach <id> paper.md\n```\n\n## Fetch Open Access\n\n```bash\n# Auto-discover and download OA full text\nref fulltext fetch <id>\n\n# Fetch with auto-attach on add\nref add 10.1234/example --fetch-fulltext\n```\n\n## Convert Formats\n\n```bash\n# Convert PDF to Markdown (requires external converter)\nref fulltext convert <id>\n\n# Supported converters: marker, docling, mineru, pymupdf\n# Configure custom converter:\nref config set fulltext.pdfConverter.command "marker"\nref config set fulltext.pdfConverter.args "{input} --output {output_dir}"\n```\n\n## Retrieve Full Text\n\n```bash\n# Get file path\nref fulltext get <id>\n\n# Get Markdown content to stdout\nref fulltext get <id> --stdout\n\n# Prefer specific format\nref fulltext get <id> --markdown\nref fulltext get <id> --pdf\n\n# Open in default system viewer\nref fulltext open <id>\n```\n\n## Check Availability\n\n```bash\n# Discover OA sources without downloading\nref fulltext discover <id>\n```\n\n## Detach\n\n```bash\n# Remove full-text association (file remains on disk)\nref fulltext detach <id>\n```\n\n## Tips\n\n- Use `ref fulltext get <id> --stdout` to pipe content to other tools\n- Full-text files are stored in the attachments directory (see `ref config get attachments.directory`)\n- Use `ref show <id>` to see full-text status alongside metadata\n- Markdown is preferred for AI-readable content; use `ref fulltext convert` after attaching PDF\n';
|
|
11296
|
+
const manuscriptWritingMd = "---\nname: ref/manuscript-writing\ndescription: Workflow for managing references while writing academic manuscripts. Covers citation generation, bibliography export, and reference verification.\n---\n\n# Manuscript Writing Workflow\n\nGuide for managing references during academic manuscript writing with `ref`.\n\n## 1. Find and Cite\n\n```bash\n# Search your library\nref search \"Smith 2024\"\n\n# Generate an inline citation\nref cite <id>\n\n# Generate citation in specific style\nref cite <id> --style apa\nref cite <id> --style vancouver\n\n# Get Pandoc/LaTeX citation key\nref cite <id> --key\n```\n\n## 2. Build Bibliography\n\n```bash\n# Export all cited references as BibTeX\nref export <id1> <id2> <id3> --output bibtex > references.bib\n\n# Export by search query\nref search \"tag:cited\" --output bibtex > references.bib\n\n# Export all references\nref export --all --output bibtex > library.bib\n```\n\n## 3. Add New References During Writing\n\n```bash\n# Quick add by DOI from a paper you're reading\nref add 10.1234/example\n\n# Add from clipboard BibTeX (pipe from stdin)\necho '<bibtex>' | ref add -\n\n# Add and immediately get citation key\nref add 10.1234/example --json | jq -r '.added[0].id'\n```\n\n## 4. Verify Before Submission\n\n```bash\n# Check all cited references for retractions\nref check <id1> <id2> <id3>\n\n# Verify metadata accuracy\nref check <id> --metadata\n\n# Show full details for final review\nref show <id>\n```\n\n## 5. Full-Text Management\n\n```bash\n# Attach supplementary files\nref fulltext attach <id> paper.pdf\nref fulltext attach <id> supplement.pdf\n\n# Get full-text path for reading\nref fulltext get <id>\n\n# Open in default viewer\nref fulltext open <id>\n```\n\n## Tips\n\n- Use `ref cite <id> --key` to get `@AuthorYear` keys for Pandoc\n- Use `ref show <id> --output yaml` for quick metadata overview\n- Use `ref url <id>` to get DOI/PubMed URLs for linking\n- Tag references with `ref update <id> --set \"tags=cited\"` to track what's in the manuscript\n";
|
|
11297
|
+
const systematicReviewMd = '---\nname: ref/systematic-review\ndescription: Workflow for conducting systematic literature reviews using reference-manager. Covers search strategy, screening, and evidence synthesis.\n---\n\n# Systematic Review Workflow\n\nGuide for conducting systematic literature reviews with `ref`.\n\n## 1. Build Search Set\n\nAdd references from multiple sources:\n\n```bash\n# From DOIs (e.g., exported from database search)\nref add 10.1234/study1 10.1234/study2 10.1234/study3\n\n# From BibTeX exported by PubMed/Scopus/Web of Science\nref add pubmed-results.bib\nref add scopus-results.ris\n\n# By PMID list\nref add 12345678 23456789 34567890\n```\n\n## 2. Tag and Organize\n\nUse tags to track screening stages:\n\n```bash\n# Tag references for screening\nref update <id> --set "tags=screening"\n\n# After title/abstract screening\nref update <id> --set "tags=included"\nref update <id> --set "tags=excluded"\n\n# After full-text review\nref update <id> --set "tags=final-included"\n```\n\n## 3. Full-Text Collection\n\n```bash\n# Auto-fetch open access full texts\nref fulltext fetch <id>\n\n# Attach manually obtained PDFs\nref fulltext attach <id> path/to/paper.pdf\n\n# Convert PDF to Markdown for text analysis\nref fulltext convert <id>\n```\n\n## 4. Quality Check\n\n```bash\n# Check for retractions or corrections\nref check <id>\n\n# Verify metadata against upstream sources\nref check <id> --metadata\n```\n\n## 5. Export for Analysis\n\n```bash\n# Export included studies as BibTeX\nref search "tag:final-included" --output bibtex > included.bib\n\n# Export as JSON for custom analysis\nref search "tag:final-included" --json > included.json\n```\n\n## Tips\n\n- Use `ref list --sort date-desc` to review chronologically\n- Use `ref search "tag:screening"` to find unprocessed references\n- Use `ref show <id>` to inspect full metadata before decisions\n- Duplicates are auto-detected on `ref add` — review skip messages\n';
|
|
11298
|
+
const SKILL_FILES = [
|
|
11299
|
+
["SKILL.md", skillMd],
|
|
11300
|
+
["references/systematic-review.md", systematicReviewMd],
|
|
11301
|
+
["references/manuscript-writing.md", manuscriptWritingMd],
|
|
11302
|
+
["references/fulltext.md", fulltextMd]
|
|
11303
|
+
];
|
|
11304
|
+
async function writeSkills(options) {
|
|
11305
|
+
const { targetDir, force = false, user = false } = options;
|
|
11306
|
+
const baseDir = user ? homedir() : targetDir;
|
|
11307
|
+
const agentsDir = join(baseDir, ".agents", "skills", "ref");
|
|
11308
|
+
const claudeDir = join(baseDir, ".claude", "skills");
|
|
11309
|
+
const claudeLink = join(claudeDir, "ref");
|
|
11310
|
+
const written = [];
|
|
11311
|
+
const skipped = [];
|
|
11312
|
+
mkdirSync(join(agentsDir, "references"), { recursive: true });
|
|
11313
|
+
for (const [relativePath, content] of SKILL_FILES) {
|
|
11314
|
+
const filePath = join(agentsDir, relativePath);
|
|
11315
|
+
if (existsSync(filePath) && !force) {
|
|
11316
|
+
skipped.push(relativePath);
|
|
11317
|
+
continue;
|
|
11318
|
+
}
|
|
11319
|
+
writeFileSync(filePath, content, "utf-8");
|
|
11320
|
+
written.push(relativePath);
|
|
11321
|
+
}
|
|
11322
|
+
let linkCreated = false;
|
|
11323
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
11324
|
+
if (force && existsSync(claudeLink)) {
|
|
11325
|
+
const stat2 = lstatSync(claudeLink);
|
|
11326
|
+
if (stat2.isSymbolicLink()) {
|
|
11327
|
+
unlinkSync(claudeLink);
|
|
11328
|
+
} else if (stat2.isDirectory()) {
|
|
11329
|
+
rmSync(claudeLink, { recursive: true, force: true });
|
|
11330
|
+
}
|
|
11331
|
+
}
|
|
11332
|
+
if (!existsSync(claudeLink)) {
|
|
11333
|
+
const linkTarget = relative(claudeDir, agentsDir);
|
|
11334
|
+
symlinkSync(linkTarget, claudeLink, "junction");
|
|
11335
|
+
linkCreated = true;
|
|
11336
|
+
}
|
|
11337
|
+
return { written, skipped, linkCreated };
|
|
11338
|
+
}
|
|
11339
|
+
async function executeInstallSkills(options) {
|
|
11340
|
+
return writeSkills({
|
|
11341
|
+
targetDir: process.cwd(),
|
|
11342
|
+
force: options.force,
|
|
11343
|
+
...options.user != null && { user: options.user }
|
|
11344
|
+
});
|
|
11345
|
+
}
|
|
11346
|
+
function formatInstallSkillsOutput(result) {
|
|
11347
|
+
const lines = [];
|
|
11348
|
+
if (result.written.length === 0 && result.skipped.length > 0) {
|
|
11349
|
+
lines.push("Skills already up-to-date. Use --force to overwrite.");
|
|
11350
|
+
lines.push(` ${result.skipped.length} files skipped`);
|
|
11351
|
+
return lines.join("\n");
|
|
11352
|
+
}
|
|
11353
|
+
if (result.written.length > 0) {
|
|
11354
|
+
lines.push(`Installed ${result.written.length} skill files to .agents/skills/ref/`);
|
|
11355
|
+
for (const file of result.written) {
|
|
11356
|
+
lines.push(` + ${file}`);
|
|
11357
|
+
}
|
|
11358
|
+
}
|
|
11359
|
+
if (result.skipped.length > 0) {
|
|
11360
|
+
lines.push(` ${result.skipped.length} files skipped (already exist)`);
|
|
11361
|
+
}
|
|
11362
|
+
if (result.linkCreated) {
|
|
11363
|
+
lines.push("Created symlink: .claude/skills/ref -> .agents/skills/ref");
|
|
11364
|
+
}
|
|
11365
|
+
return lines.join("\n");
|
|
11366
|
+
}
|
|
11293
11367
|
function buildResourceIndicators(item) {
|
|
11294
11368
|
const labels = [];
|
|
11295
11369
|
const attachments = item.custom?.attachments;
|
|
@@ -26836,49 +26910,49 @@ function requireFastUri() {
|
|
|
26836
26910
|
schemelessOptions.skipEscape = true;
|
|
26837
26911
|
return serialize2(resolved, schemelessOptions);
|
|
26838
26912
|
}
|
|
26839
|
-
function resolveComponent(base,
|
|
26913
|
+
function resolveComponent(base, relative2, options, skipNormalization) {
|
|
26840
26914
|
const target = {};
|
|
26841
26915
|
if (!skipNormalization) {
|
|
26842
26916
|
base = parse2(serialize2(base, options), options);
|
|
26843
|
-
|
|
26917
|
+
relative2 = parse2(serialize2(relative2, options), options);
|
|
26844
26918
|
}
|
|
26845
26919
|
options = options || {};
|
|
26846
|
-
if (!options.tolerant &&
|
|
26847
|
-
target.scheme =
|
|
26848
|
-
target.userinfo =
|
|
26849
|
-
target.host =
|
|
26850
|
-
target.port =
|
|
26851
|
-
target.path = removeDotSegments(
|
|
26852
|
-
target.query =
|
|
26920
|
+
if (!options.tolerant && relative2.scheme) {
|
|
26921
|
+
target.scheme = relative2.scheme;
|
|
26922
|
+
target.userinfo = relative2.userinfo;
|
|
26923
|
+
target.host = relative2.host;
|
|
26924
|
+
target.port = relative2.port;
|
|
26925
|
+
target.path = removeDotSegments(relative2.path || "");
|
|
26926
|
+
target.query = relative2.query;
|
|
26853
26927
|
} else {
|
|
26854
|
-
if (
|
|
26855
|
-
target.userinfo =
|
|
26856
|
-
target.host =
|
|
26857
|
-
target.port =
|
|
26858
|
-
target.path = removeDotSegments(
|
|
26859
|
-
target.query =
|
|
26928
|
+
if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
|
|
26929
|
+
target.userinfo = relative2.userinfo;
|
|
26930
|
+
target.host = relative2.host;
|
|
26931
|
+
target.port = relative2.port;
|
|
26932
|
+
target.path = removeDotSegments(relative2.path || "");
|
|
26933
|
+
target.query = relative2.query;
|
|
26860
26934
|
} else {
|
|
26861
|
-
if (!
|
|
26935
|
+
if (!relative2.path) {
|
|
26862
26936
|
target.path = base.path;
|
|
26863
|
-
if (
|
|
26864
|
-
target.query =
|
|
26937
|
+
if (relative2.query !== void 0) {
|
|
26938
|
+
target.query = relative2.query;
|
|
26865
26939
|
} else {
|
|
26866
26940
|
target.query = base.query;
|
|
26867
26941
|
}
|
|
26868
26942
|
} else {
|
|
26869
|
-
if (
|
|
26870
|
-
target.path = removeDotSegments(
|
|
26943
|
+
if (relative2.path[0] === "/") {
|
|
26944
|
+
target.path = removeDotSegments(relative2.path);
|
|
26871
26945
|
} else {
|
|
26872
26946
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
26873
|
-
target.path = "/" +
|
|
26947
|
+
target.path = "/" + relative2.path;
|
|
26874
26948
|
} else if (!base.path) {
|
|
26875
|
-
target.path =
|
|
26949
|
+
target.path = relative2.path;
|
|
26876
26950
|
} else {
|
|
26877
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
26951
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
|
|
26878
26952
|
}
|
|
26879
26953
|
target.path = removeDotSegments(target.path);
|
|
26880
26954
|
}
|
|
26881
|
-
target.query =
|
|
26955
|
+
target.query = relative2.query;
|
|
26882
26956
|
}
|
|
26883
26957
|
target.userinfo = base.userinfo;
|
|
26884
26958
|
target.host = base.host;
|
|
@@ -26886,7 +26960,7 @@ function requireFastUri() {
|
|
|
26886
26960
|
}
|
|
26887
26961
|
target.scheme = base.scheme;
|
|
26888
26962
|
}
|
|
26889
|
-
target.fragment =
|
|
26963
|
+
target.fragment = relative2.fragment;
|
|
26890
26964
|
return target;
|
|
26891
26965
|
}
|
|
26892
26966
|
function equal2(uriA, uriB, options) {
|
|
@@ -32451,7 +32525,7 @@ async function mcpStart(options) {
|
|
|
32451
32525
|
async function executeRemove(options, context) {
|
|
32452
32526
|
const { identifier, idType = "id", fulltextDirectory, deleteFulltext = false } = options;
|
|
32453
32527
|
if (context.mode === "local" && deleteFulltext && fulltextDirectory) {
|
|
32454
|
-
const { removeReference } = await import("./index-
|
|
32528
|
+
const { removeReference } = await import("./index-B4-i4PrU.js").then((n) => n.A);
|
|
32455
32529
|
return removeReference(context.library, {
|
|
32456
32530
|
identifier,
|
|
32457
32531
|
idType,
|
|
@@ -32506,7 +32580,7 @@ Continue?`;
|
|
|
32506
32580
|
}
|
|
32507
32581
|
async function executeInteractiveRemove(context, config2) {
|
|
32508
32582
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
32509
|
-
const { selectReferenceItemsOrExit } = await import("./reference-select-
|
|
32583
|
+
const { selectReferenceItemsOrExit } = await import("./reference-select-B9b9Ez7Q.js");
|
|
32510
32584
|
const allReferences = await context.library.getAll();
|
|
32511
32585
|
const selectedItems = await withAlternateScreen2(
|
|
32512
32586
|
() => selectReferenceItemsOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
|
|
@@ -32731,7 +32805,7 @@ async function executeInteractiveSearch(options, context, config2) {
|
|
|
32731
32805
|
validateInteractiveOptions(options);
|
|
32732
32806
|
const { checkTTY } = await import("./tty-BMyaEOhX.js");
|
|
32733
32807
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
32734
|
-
const { runSearchFlow } = await import("./index-
|
|
32808
|
+
const { runSearchFlow } = await import("./index-BuQm8A5F.js");
|
|
32735
32809
|
const { search } = await import("./file-watcher-CWHg1yol.js").then((n) => n.B);
|
|
32736
32810
|
const { tokenize } = await import("./file-watcher-CWHg1yol.js").then((n) => n.A);
|
|
32737
32811
|
checkTTY();
|
|
@@ -32750,7 +32824,7 @@ async function executeInteractiveSearch(options, context, config2) {
|
|
|
32750
32824
|
})
|
|
32751
32825
|
);
|
|
32752
32826
|
if (result.selectedItems && !result.cancelled) {
|
|
32753
|
-
const { isSideEffectAction } = await import("./action-menu-
|
|
32827
|
+
const { isSideEffectAction } = await import("./action-menu-DLEwSKrj.js");
|
|
32754
32828
|
if (isSideEffectAction(result.action)) {
|
|
32755
32829
|
await executeSideEffectAction(result.action, result.selectedItems, context, config2);
|
|
32756
32830
|
return { output: "", cancelled: false, action: result.action };
|
|
@@ -33048,7 +33122,7 @@ function formatShowOutput(item, options, attachmentsDirectory) {
|
|
|
33048
33122
|
}
|
|
33049
33123
|
async function executeInteractiveSelect$1(context, config2) {
|
|
33050
33124
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
33051
|
-
const { selectReferencesOrExit } = await import("./reference-select-
|
|
33125
|
+
const { selectReferencesOrExit } = await import("./reference-select-B9b9Ez7Q.js");
|
|
33052
33126
|
const allReferences = await context.library.getAll();
|
|
33053
33127
|
const identifiers = await withAlternateScreen2(
|
|
33054
33128
|
() => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
|
|
@@ -33297,7 +33371,7 @@ function formatUpdateOutput(result, identifier) {
|
|
|
33297
33371
|
}
|
|
33298
33372
|
async function executeInteractiveUpdate(context, config2) {
|
|
33299
33373
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
33300
|
-
const { selectReferencesOrExit } = await import("./reference-select-
|
|
33374
|
+
const { selectReferencesOrExit } = await import("./reference-select-B9b9Ez7Q.js");
|
|
33301
33375
|
const allReferences = await context.library.getAll();
|
|
33302
33376
|
const identifiers = await withAlternateScreen2(
|
|
33303
33377
|
() => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
|
|
@@ -33592,7 +33666,7 @@ function getUrlExitCode(result) {
|
|
|
33592
33666
|
}
|
|
33593
33667
|
async function executeInteractiveSelect(context, config2) {
|
|
33594
33668
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
33595
|
-
const { selectReferencesOrExit } = await import("./reference-select-
|
|
33669
|
+
const { selectReferencesOrExit } = await import("./reference-select-B9b9Ez7Q.js");
|
|
33596
33670
|
const allReferences = await context.library.getAll();
|
|
33597
33671
|
const identifiers = await withAlternateScreen2(
|
|
33598
33672
|
() => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
|
|
@@ -34040,6 +34114,7 @@ function createProgram() {
|
|
|
34040
34114
|
registerAttachCommand(program);
|
|
34041
34115
|
registerMcpCommand(program);
|
|
34042
34116
|
registerUrlCommand(program);
|
|
34117
|
+
registerInstallCommand(program);
|
|
34043
34118
|
registerConfigCommand(program);
|
|
34044
34119
|
registerCompletionCommand(program);
|
|
34045
34120
|
return program;
|
|
@@ -34204,7 +34279,7 @@ function shouldAutoFetch(cliFlag, configEnabled) {
|
|
|
34204
34279
|
return configEnabled;
|
|
34205
34280
|
}
|
|
34206
34281
|
async function performAutoFetch(addedItems, context, config2) {
|
|
34207
|
-
const { fulltextFetch: fulltextFetch2 } = await import("./index-
|
|
34282
|
+
const { fulltextFetch: fulltextFetch2 } = await import("./index-B4-i4PrU.js").then((n) => n.z);
|
|
34208
34283
|
const fetchResults = await autoFetchFulltext(addedItems, context, {
|
|
34209
34284
|
fulltextConfig: config2.fulltext,
|
|
34210
34285
|
fulltextDirectory: config2.attachments.directory,
|
|
@@ -34470,6 +34545,25 @@ function registerUrlCommand(program) {
|
|
|
34470
34545
|
await handleUrlAction(identifiers.length > 0 ? identifiers : void 0, options, globalOpts);
|
|
34471
34546
|
});
|
|
34472
34547
|
}
|
|
34548
|
+
function registerInstallCommand(program) {
|
|
34549
|
+
const installCmd = program.command("install").description("Install tools and integrations");
|
|
34550
|
+
installCmd.command("skills").description("Install Agent Skills (SKILL.md) for AI coding agents").option("-f, --force", "Overwrite existing files").option("-u, --user", "Install to user-level directory (~/.agents/skills/)").action(async (options) => {
|
|
34551
|
+
try {
|
|
34552
|
+
const result = await executeInstallSkills({
|
|
34553
|
+
force: options.force ?? false,
|
|
34554
|
+
...options.user != null && { user: options.user }
|
|
34555
|
+
});
|
|
34556
|
+
const output = formatInstallSkillsOutput(result);
|
|
34557
|
+
process.stdout.write(`${output}
|
|
34558
|
+
`);
|
|
34559
|
+
setExitCode(ExitCode.SUCCESS);
|
|
34560
|
+
} catch (error) {
|
|
34561
|
+
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}
|
|
34562
|
+
`);
|
|
34563
|
+
setExitCode(ExitCode.INTERNAL_ERROR);
|
|
34564
|
+
}
|
|
34565
|
+
});
|
|
34566
|
+
}
|
|
34473
34567
|
async function main(argv) {
|
|
34474
34568
|
const program = createProgram();
|
|
34475
34569
|
if (process.env.COMP_LINE) {
|
|
@@ -34499,4 +34593,4 @@ export {
|
|
|
34499
34593
|
restoreStdinAfterInk as r,
|
|
34500
34594
|
syncAttachments as s
|
|
34501
34595
|
};
|
|
34502
|
-
//# sourceMappingURL=index-
|
|
34596
|
+
//# sourceMappingURL=index-CmkN2-2A.js.map
|