@ncukondo/reference-manager 0.5.2 → 0.6.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 +63 -1
- package/dist/chunks/{file-watcher-Dqkw6R7-.js → file-watcher-Cwfnnw92.js} +161 -28
- package/dist/chunks/file-watcher-Cwfnnw92.js.map +1 -0
- package/dist/chunks/index-CQO8hLYm.js +1788 -0
- package/dist/chunks/index-CQO8hLYm.js.map +1 -0
- package/dist/chunks/{loader-DuzyKV70.js → loader-B_ZLxCQW.js} +97 -26
- package/dist/chunks/loader-B_ZLxCQW.js.map +1 -0
- package/dist/cli/commands/fulltext.d.ts +4 -3
- package/dist/cli/commands/fulltext.d.ts.map +1 -1
- package/dist/cli/commands/list.d.ts +7 -1
- package/dist/cli/commands/list.d.ts.map +1 -1
- package/dist/cli/commands/remove.d.ts +2 -1
- package/dist/cli/commands/remove.d.ts.map +1 -1
- package/dist/cli/commands/search.d.ts +7 -1
- package/dist/cli/commands/search.d.ts.map +1 -1
- package/dist/cli/commands/update.d.ts +2 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/completion.d.ts +65 -0
- package/dist/cli/completion.d.ts.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/server-client.d.ts +5 -4
- package/dist/cli/server-client.d.ts.map +1 -1
- package/dist/cli.js +522 -153
- package/dist/cli.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/schema.d.ts +74 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/core/library-interface.d.ts +22 -4
- package/dist/core/library-interface.d.ts.map +1 -1
- package/dist/core/library.d.ts +2 -10
- package/dist/core/library.d.ts.map +1 -1
- package/dist/core/reference.d.ts +1 -0
- package/dist/core/reference.d.ts.map +1 -1
- package/dist/features/duplicate/detector.d.ts.map +1 -1
- package/dist/features/duplicate/types.d.ts +2 -1
- package/dist/features/duplicate/types.d.ts.map +1 -1
- package/dist/features/import/cache.d.ts +8 -0
- package/dist/features/import/cache.d.ts.map +1 -1
- package/dist/features/import/detector.d.ts +11 -3
- package/dist/features/import/detector.d.ts.map +1 -1
- package/dist/features/import/fetcher.d.ts +8 -0
- package/dist/features/import/fetcher.d.ts.map +1 -1
- package/dist/features/import/importer.d.ts.map +1 -1
- package/dist/features/import/normalizer.d.ts +13 -0
- package/dist/features/import/normalizer.d.ts.map +1 -1
- package/dist/features/import/rate-limiter.d.ts +1 -1
- package/dist/features/import/rate-limiter.d.ts.map +1 -1
- package/dist/features/operations/cite.d.ts +3 -3
- package/dist/features/operations/cite.d.ts.map +1 -1
- package/dist/features/operations/fulltext/attach.d.ts +3 -3
- package/dist/features/operations/fulltext/attach.d.ts.map +1 -1
- package/dist/features/operations/fulltext/detach.d.ts +3 -3
- package/dist/features/operations/fulltext/detach.d.ts.map +1 -1
- package/dist/features/operations/fulltext/get.d.ts +3 -3
- package/dist/features/operations/fulltext/get.d.ts.map +1 -1
- package/dist/features/operations/list.d.ts +12 -3
- package/dist/features/operations/list.d.ts.map +1 -1
- package/dist/features/operations/remove.d.ts +3 -3
- package/dist/features/operations/remove.d.ts.map +1 -1
- package/dist/features/operations/search.d.ts +19 -3
- package/dist/features/operations/search.d.ts.map +1 -1
- package/dist/features/operations/update.d.ts +3 -3
- package/dist/features/operations/update.d.ts.map +1 -1
- package/dist/features/pagination/aliases.d.ts +14 -0
- package/dist/features/pagination/aliases.d.ts.map +1 -0
- package/dist/features/pagination/index.d.ts +8 -0
- package/dist/features/pagination/index.d.ts.map +1 -0
- package/dist/features/pagination/paginate.d.ts +20 -0
- package/dist/features/pagination/paginate.d.ts.map +1 -0
- package/dist/features/pagination/sorter.d.ts +16 -0
- package/dist/features/pagination/sorter.d.ts.map +1 -0
- package/dist/features/pagination/types.d.ts +74 -0
- package/dist/features/pagination/types.d.ts.map +1 -0
- package/dist/index.js +13 -12
- package/dist/index.js.map +1 -1
- package/dist/mcp/context.d.ts +4 -4
- package/dist/mcp/context.d.ts.map +1 -1
- package/dist/mcp/resources/index.d.ts +3 -3
- package/dist/mcp/resources/index.d.ts.map +1 -1
- package/dist/mcp/resources/library.d.ts +5 -5
- package/dist/mcp/resources/library.d.ts.map +1 -1
- package/dist/mcp/tools/add.d.ts +3 -3
- package/dist/mcp/tools/add.d.ts.map +1 -1
- package/dist/mcp/tools/cite.d.ts +3 -3
- package/dist/mcp/tools/cite.d.ts.map +1 -1
- package/dist/mcp/tools/fulltext.d.ts +7 -7
- package/dist/mcp/tools/fulltext.d.ts.map +1 -1
- package/dist/mcp/tools/index.d.ts +3 -3
- package/dist/mcp/tools/index.d.ts.map +1 -1
- package/dist/mcp/tools/list.d.ts +10 -3
- package/dist/mcp/tools/list.d.ts.map +1 -1
- package/dist/mcp/tools/remove.d.ts +3 -3
- package/dist/mcp/tools/remove.d.ts.map +1 -1
- package/dist/mcp/tools/search.d.ts +10 -3
- package/dist/mcp/tools/search.d.ts.map +1 -1
- package/dist/server/routes/list.d.ts +13 -0
- package/dist/server/routes/list.d.ts.map +1 -1
- package/dist/server/routes/references.d.ts.map +1 -1
- package/dist/server/routes/search.d.ts +14 -0
- package/dist/server/routes/search.d.ts.map +1 -1
- package/dist/server.js +4 -4
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/object.d.ts +16 -0
- package/dist/utils/object.d.ts.map +1 -0
- package/package.json +4 -1
- package/dist/chunks/file-watcher-Dqkw6R7-.js.map +0 -1
- package/dist/chunks/index-D4Q13N-R.js +0 -29863
- package/dist/chunks/index-D4Q13N-R.js.map +0 -1
- package/dist/chunks/loader-DuzyKV70.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { ZodOptional as ZodOptional$2, z } from "zod";
|
|
3
|
-
import { L as Library, F as FileWatcher } from "./chunks/file-watcher-
|
|
3
|
+
import { p as pickDefined, q as sortOrderSchema, r as paginationOptionsSchema, L as Library, F as FileWatcher, u as sortFieldSchema, v as searchSortFieldSchema } from "./chunks/file-watcher-Cwfnnw92.js";
|
|
4
4
|
import { promises, existsSync, mkdtempSync, writeFileSync, readFileSync } from "node:fs";
|
|
5
5
|
import * as os from "node:os";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import { join, extname } from "node:path";
|
|
9
9
|
import { mkdir, unlink, rename, copyFile, rm, readFile } from "node:fs/promises";
|
|
10
|
-
import { u as updateReference,
|
|
10
|
+
import { u as updateReference, B as BUILTIN_STYLES, s as startServerWithFileWatcher } from "./chunks/index-CQO8hLYm.js";
|
|
11
11
|
import process$1, { stdin, stdout } from "node:process";
|
|
12
|
-
import { l as loadConfig } from "./chunks/loader-
|
|
12
|
+
import { l as loadConfig } from "./chunks/loader-B_ZLxCQW.js";
|
|
13
13
|
import { spawn } from "node:child_process";
|
|
14
14
|
import { serve } from "@hono/node-server";
|
|
15
15
|
const name = "@ncukondo/reference-manager";
|
|
16
|
-
const version$1 = "0.
|
|
16
|
+
const version$1 = "0.6.0";
|
|
17
17
|
const description$1 = "A local reference management tool using CSL-JSON as the single source of truth";
|
|
18
18
|
const packageJson = {
|
|
19
19
|
name,
|
|
@@ -163,7 +163,7 @@ async function validateOptions$2(options) {
|
|
|
163
163
|
function buildCiteOptions(options) {
|
|
164
164
|
return {
|
|
165
165
|
identifiers: options.identifiers,
|
|
166
|
-
...options.uuid
|
|
166
|
+
...options.uuid && { idType: "uuid" },
|
|
167
167
|
...options.style !== void 0 && { style: options.style },
|
|
168
168
|
...options.cslFile !== void 0 && { cslFile: options.cslFile },
|
|
169
169
|
...options.locale !== void 0 && { locale: options.locale },
|
|
@@ -451,11 +451,11 @@ async function fulltextAttach(library, options) {
|
|
|
451
451
|
type: explicitType,
|
|
452
452
|
move,
|
|
453
453
|
force,
|
|
454
|
-
|
|
454
|
+
idType = "id",
|
|
455
455
|
fulltextDirectory,
|
|
456
456
|
stdinContent
|
|
457
457
|
} = options;
|
|
458
|
-
const item = await library.find(identifier, {
|
|
458
|
+
const item = await library.find(identifier, { idType });
|
|
459
459
|
if (!item) {
|
|
460
460
|
return { success: false, error: `Reference '${identifier}' not found` };
|
|
461
461
|
}
|
|
@@ -482,7 +482,7 @@ async function fulltextAttach(library, options) {
|
|
|
482
482
|
updates: {
|
|
483
483
|
custom: { fulltext: newFulltext }
|
|
484
484
|
},
|
|
485
|
-
|
|
485
|
+
idType
|
|
486
486
|
});
|
|
487
487
|
await cleanupTempDir(tempDir);
|
|
488
488
|
return {
|
|
@@ -528,8 +528,8 @@ function getFilePaths(manager, item, types2, identifier) {
|
|
|
528
528
|
return { success: true, paths };
|
|
529
529
|
}
|
|
530
530
|
async function fulltextGet(library, options) {
|
|
531
|
-
const { identifier, type: type2, stdout: stdout2,
|
|
532
|
-
const item = await library.find(identifier, {
|
|
531
|
+
const { identifier, type: type2, stdout: stdout2, idType = "id", fulltextDirectory } = options;
|
|
532
|
+
const item = await library.find(identifier, { idType });
|
|
533
533
|
if (!item) {
|
|
534
534
|
return { success: false, error: `Reference '${identifier}' not found` };
|
|
535
535
|
}
|
|
@@ -573,8 +573,8 @@ function handleDetachError(error) {
|
|
|
573
573
|
throw error;
|
|
574
574
|
}
|
|
575
575
|
async function fulltextDetach(library, options) {
|
|
576
|
-
const { identifier, type: type2, delete: deleteFile,
|
|
577
|
-
const item = await library.find(identifier, {
|
|
576
|
+
const { identifier, type: type2, delete: deleteFile, idType = "id", fulltextDirectory } = options;
|
|
577
|
+
const item = await library.find(identifier, { idType });
|
|
578
578
|
if (!item) {
|
|
579
579
|
return { success: false, error: `Reference '${identifier}' not found` };
|
|
580
580
|
}
|
|
@@ -596,7 +596,7 @@ async function fulltextDetach(library, options) {
|
|
|
596
596
|
updates: {
|
|
597
597
|
custom: { fulltext: updatedFulltext }
|
|
598
598
|
},
|
|
599
|
-
|
|
599
|
+
idType
|
|
600
600
|
});
|
|
601
601
|
const resultData = { success: true, detached };
|
|
602
602
|
if (deleted.length > 0) {
|
|
@@ -614,7 +614,7 @@ async function executeFulltextAttach(options, context) {
|
|
|
614
614
|
type: options.type,
|
|
615
615
|
move: options.move,
|
|
616
616
|
force: options.force,
|
|
617
|
-
|
|
617
|
+
idType: options.idType,
|
|
618
618
|
fulltextDirectory: options.fulltextDirectory,
|
|
619
619
|
stdinContent: options.stdinContent
|
|
620
620
|
};
|
|
@@ -625,7 +625,7 @@ async function executeFulltextGet(options, context) {
|
|
|
625
625
|
identifier: options.identifier,
|
|
626
626
|
type: options.type,
|
|
627
627
|
stdout: options.stdout,
|
|
628
|
-
|
|
628
|
+
idType: options.idType,
|
|
629
629
|
fulltextDirectory: options.fulltextDirectory
|
|
630
630
|
};
|
|
631
631
|
return fulltextGet(context.library, operationOptions);
|
|
@@ -635,7 +635,7 @@ async function executeFulltextDetach(options, context) {
|
|
|
635
635
|
identifier: options.identifier,
|
|
636
636
|
type: options.type,
|
|
637
637
|
delete: options.delete,
|
|
638
|
-
|
|
638
|
+
idType: options.idType,
|
|
639
639
|
fulltextDirectory: options.fulltextDirectory
|
|
640
640
|
};
|
|
641
641
|
return fulltextDetach(context.library, operationOptions);
|
|
@@ -689,6 +689,40 @@ function formatFulltextDetachOutput(result) {
|
|
|
689
689
|
function getFulltextExitCode(result) {
|
|
690
690
|
return result.success ? 0 : 1;
|
|
691
691
|
}
|
|
692
|
+
const SORT_ALIASES = {
|
|
693
|
+
pub: "published",
|
|
694
|
+
mod: "updated",
|
|
695
|
+
add: "created",
|
|
696
|
+
rel: "relevance"
|
|
697
|
+
};
|
|
698
|
+
const VALID_SORT_FIELDS = /* @__PURE__ */ new Set([
|
|
699
|
+
"created",
|
|
700
|
+
"updated",
|
|
701
|
+
"published",
|
|
702
|
+
"author",
|
|
703
|
+
"title",
|
|
704
|
+
"relevance"
|
|
705
|
+
]);
|
|
706
|
+
function resolveSortAlias(alias) {
|
|
707
|
+
if (VALID_SORT_FIELDS.has(alias)) {
|
|
708
|
+
return alias;
|
|
709
|
+
}
|
|
710
|
+
const resolved = SORT_ALIASES[alias];
|
|
711
|
+
if (resolved) {
|
|
712
|
+
return resolved;
|
|
713
|
+
}
|
|
714
|
+
throw new Error(`Unknown sort field: ${alias}`);
|
|
715
|
+
}
|
|
716
|
+
const VALID_LIST_SORT_FIELDS = /* @__PURE__ */ new Set([
|
|
717
|
+
"created",
|
|
718
|
+
"updated",
|
|
719
|
+
"published",
|
|
720
|
+
"author",
|
|
721
|
+
"title",
|
|
722
|
+
"add",
|
|
723
|
+
"mod",
|
|
724
|
+
"pub"
|
|
725
|
+
]);
|
|
692
726
|
function getListFormat(options) {
|
|
693
727
|
if (options.json) return "json";
|
|
694
728
|
if (options.idsOnly) return "ids-only";
|
|
@@ -705,17 +739,58 @@ function validateOptions$1(options) {
|
|
|
705
739
|
"Multiple output formats specified. Only one of --json, --ids-only, --uuid, --bibtex can be used."
|
|
706
740
|
);
|
|
707
741
|
}
|
|
742
|
+
if (options.sort !== void 0) {
|
|
743
|
+
const sortStr = String(options.sort);
|
|
744
|
+
if (!VALID_LIST_SORT_FIELDS.has(sortStr)) {
|
|
745
|
+
throw new Error(`Invalid sort field: ${sortStr}`);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
if (options.order !== void 0) {
|
|
749
|
+
const result = sortOrderSchema.safeParse(options.order);
|
|
750
|
+
if (!result.success) {
|
|
751
|
+
throw new Error(`Invalid sort order: ${options.order}`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
const paginationResult = paginationOptionsSchema.safeParse({
|
|
755
|
+
limit: options.limit,
|
|
756
|
+
offset: options.offset
|
|
757
|
+
});
|
|
758
|
+
if (!paginationResult.success) {
|
|
759
|
+
const issue2 = paginationResult.error.issues[0];
|
|
760
|
+
throw new Error(`Invalid pagination option: ${issue2?.message ?? "unknown error"}`);
|
|
761
|
+
}
|
|
708
762
|
}
|
|
709
763
|
async function executeList(options, context) {
|
|
710
764
|
validateOptions$1(options);
|
|
711
765
|
const format2 = getListFormat(options);
|
|
712
|
-
|
|
766
|
+
const sort = options.sort ? resolveSortAlias(options.sort) : void 0;
|
|
767
|
+
return context.library.list({
|
|
768
|
+
format: format2,
|
|
769
|
+
...sort !== void 0 && { sort },
|
|
770
|
+
...pickDefined(options, ["order", "limit", "offset"])
|
|
771
|
+
});
|
|
713
772
|
}
|
|
714
|
-
function formatListOutput(result) {
|
|
773
|
+
function formatListOutput(result, isJson = false) {
|
|
774
|
+
if (isJson) {
|
|
775
|
+
return JSON.stringify({
|
|
776
|
+
items: result.items,
|
|
777
|
+
total: result.total,
|
|
778
|
+
limit: result.limit,
|
|
779
|
+
offset: result.offset,
|
|
780
|
+
nextOffset: result.nextOffset
|
|
781
|
+
});
|
|
782
|
+
}
|
|
715
783
|
if (result.items.length === 0) {
|
|
716
784
|
return "";
|
|
717
785
|
}
|
|
718
|
-
|
|
786
|
+
const lines = [];
|
|
787
|
+
if (result.limit > 0 && result.total > 0) {
|
|
788
|
+
const start = result.offset + 1;
|
|
789
|
+
const end = result.offset + result.items.length;
|
|
790
|
+
lines.push(`# Showing ${start}-${end} of ${result.total} references`);
|
|
791
|
+
}
|
|
792
|
+
lines.push(...result.items);
|
|
793
|
+
return lines.join("\n");
|
|
719
794
|
}
|
|
720
795
|
var util$1;
|
|
721
796
|
(function(util2) {
|
|
@@ -12494,6 +12569,9 @@ function mergeCapabilities(base, additional) {
|
|
|
12494
12569
|
}
|
|
12495
12570
|
return result;
|
|
12496
12571
|
}
|
|
12572
|
+
function getDefaultExportFromCjs(x) {
|
|
12573
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
12574
|
+
}
|
|
12497
12575
|
var ajv = { exports: {} };
|
|
12498
12576
|
var core$1 = {};
|
|
12499
12577
|
var validate = {};
|
|
@@ -20856,12 +20934,54 @@ class StdioServerTransport {
|
|
|
20856
20934
|
});
|
|
20857
20935
|
}
|
|
20858
20936
|
}
|
|
20937
|
+
class OperationsLibrary {
|
|
20938
|
+
constructor(library) {
|
|
20939
|
+
this.library = library;
|
|
20940
|
+
}
|
|
20941
|
+
// ILibrary delegation
|
|
20942
|
+
find(identifier, options) {
|
|
20943
|
+
return this.library.find(identifier, options);
|
|
20944
|
+
}
|
|
20945
|
+
getAll() {
|
|
20946
|
+
return this.library.getAll();
|
|
20947
|
+
}
|
|
20948
|
+
add(item) {
|
|
20949
|
+
return this.library.add(item);
|
|
20950
|
+
}
|
|
20951
|
+
update(idOrUuid, updates, options) {
|
|
20952
|
+
return this.library.update(idOrUuid, updates, options);
|
|
20953
|
+
}
|
|
20954
|
+
remove(identifier, options) {
|
|
20955
|
+
return this.library.remove(identifier, options);
|
|
20956
|
+
}
|
|
20957
|
+
save() {
|
|
20958
|
+
return this.library.save();
|
|
20959
|
+
}
|
|
20960
|
+
// High-level operations
|
|
20961
|
+
async search(options) {
|
|
20962
|
+
const { searchReferences } = await import("./chunks/index-CQO8hLYm.js").then((n) => n.d);
|
|
20963
|
+
return searchReferences(this.library, options);
|
|
20964
|
+
}
|
|
20965
|
+
async list(options) {
|
|
20966
|
+
const { listReferences } = await import("./chunks/index-CQO8hLYm.js").then((n) => n.l);
|
|
20967
|
+
return listReferences(this.library, options ?? {});
|
|
20968
|
+
}
|
|
20969
|
+
async cite(options) {
|
|
20970
|
+
const { citeReferences } = await import("./chunks/index-CQO8hLYm.js").then((n) => n.b);
|
|
20971
|
+
return citeReferences(this.library, options);
|
|
20972
|
+
}
|
|
20973
|
+
async import(inputs, options) {
|
|
20974
|
+
const { addReferences } = await import("./chunks/index-CQO8hLYm.js").then((n) => n.a);
|
|
20975
|
+
return addReferences(inputs, this.library, options ?? {});
|
|
20976
|
+
}
|
|
20977
|
+
}
|
|
20859
20978
|
async function createMcpContext(options) {
|
|
20860
20979
|
const config2 = await loadConfig({
|
|
20861
20980
|
userConfigPath: options.configPath
|
|
20862
20981
|
});
|
|
20863
20982
|
const libraryPath = options.libraryPath ?? config2.library;
|
|
20864
20983
|
const library = await Library.load(libraryPath);
|
|
20984
|
+
const libraryOperations = new OperationsLibrary(library);
|
|
20865
20985
|
const fileWatcher = new FileWatcher(libraryPath, {
|
|
20866
20986
|
debounceMs: config2.watch.debounceMs,
|
|
20867
20987
|
maxRetries: config2.watch.maxRetries,
|
|
@@ -20877,13 +20997,13 @@ async function createMcpContext(options) {
|
|
|
20877
20997
|
fileWatcher.close();
|
|
20878
20998
|
};
|
|
20879
20999
|
return {
|
|
20880
|
-
|
|
21000
|
+
libraryOperations,
|
|
20881
21001
|
config: config2,
|
|
20882
21002
|
fileWatcher,
|
|
20883
21003
|
dispose
|
|
20884
21004
|
};
|
|
20885
21005
|
}
|
|
20886
|
-
function registerReferencesResource(server,
|
|
21006
|
+
function registerReferencesResource(server, getLibraryOperations) {
|
|
20887
21007
|
server.registerResource(
|
|
20888
21008
|
"references",
|
|
20889
21009
|
"library://references",
|
|
@@ -20892,8 +21012,8 @@ function registerReferencesResource(server, getLibrary) {
|
|
|
20892
21012
|
mimeType: "application/json"
|
|
20893
21013
|
},
|
|
20894
21014
|
async (uri2) => {
|
|
20895
|
-
const
|
|
20896
|
-
const items2 = await
|
|
21015
|
+
const libraryOps = getLibraryOperations();
|
|
21016
|
+
const items2 = await libraryOps.getAll();
|
|
20897
21017
|
return {
|
|
20898
21018
|
contents: [
|
|
20899
21019
|
{
|
|
@@ -20906,11 +21026,11 @@ function registerReferencesResource(server, getLibrary) {
|
|
|
20906
21026
|
}
|
|
20907
21027
|
);
|
|
20908
21028
|
}
|
|
20909
|
-
function registerReferenceResource(server,
|
|
21029
|
+
function registerReferenceResource(server, getLibraryOperations) {
|
|
20910
21030
|
const template = new ResourceTemplate("library://reference/{id}", {
|
|
20911
21031
|
list: async () => {
|
|
20912
|
-
const
|
|
20913
|
-
const items2 = await
|
|
21032
|
+
const libraryOps = getLibraryOperations();
|
|
21033
|
+
const items2 = await libraryOps.getAll();
|
|
20914
21034
|
return {
|
|
20915
21035
|
resources: items2.map((item) => ({
|
|
20916
21036
|
uri: `library://reference/${item.id}`,
|
|
@@ -20927,9 +21047,9 @@ function registerReferenceResource(server, getLibrary) {
|
|
|
20927
21047
|
mimeType: "application/json"
|
|
20928
21048
|
},
|
|
20929
21049
|
async (uri2, variables) => {
|
|
20930
|
-
const
|
|
21050
|
+
const libraryOps = getLibraryOperations();
|
|
20931
21051
|
const id2 = variables.id;
|
|
20932
|
-
const item = await
|
|
21052
|
+
const item = await libraryOps.find(id2);
|
|
20933
21053
|
if (!item) {
|
|
20934
21054
|
throw new Error(`Reference not found: ${id2}`);
|
|
20935
21055
|
}
|
|
@@ -20970,9 +21090,9 @@ function registerStylesResource(server) {
|
|
|
20970
21090
|
}
|
|
20971
21091
|
);
|
|
20972
21092
|
}
|
|
20973
|
-
function registerAllResources(server,
|
|
20974
|
-
registerReferencesResource(server,
|
|
20975
|
-
registerReferenceResource(server,
|
|
21093
|
+
function registerAllResources(server, getLibraryOperations) {
|
|
21094
|
+
registerReferencesResource(server, getLibraryOperations);
|
|
21095
|
+
registerReferenceResource(server, getLibraryOperations);
|
|
20976
21096
|
registerStylesResource(server);
|
|
20977
21097
|
}
|
|
20978
21098
|
function formatAddResult(result) {
|
|
@@ -21001,7 +21121,7 @@ function formatAddResult(result) {
|
|
|
21001
21121
|
}
|
|
21002
21122
|
return lines.join("\n");
|
|
21003
21123
|
}
|
|
21004
|
-
function registerAddTool(server,
|
|
21124
|
+
function registerAddTool(server, getLibraryOperations) {
|
|
21005
21125
|
server.registerTool(
|
|
21006
21126
|
"add",
|
|
21007
21127
|
{
|
|
@@ -21011,10 +21131,10 @@ function registerAddTool(server, getLibrary) {
|
|
|
21011
21131
|
}
|
|
21012
21132
|
},
|
|
21013
21133
|
async (args) => {
|
|
21014
|
-
const
|
|
21134
|
+
const libraryOps = getLibraryOperations();
|
|
21015
21135
|
const inputs = Array.isArray(args.input) ? args.input : [args.input];
|
|
21016
21136
|
const stdinContent = inputs.join("\n");
|
|
21017
|
-
const result = await
|
|
21137
|
+
const result = await libraryOps.import([], { stdinContent });
|
|
21018
21138
|
return {
|
|
21019
21139
|
content: [
|
|
21020
21140
|
{
|
|
@@ -21026,7 +21146,7 @@ function registerAddTool(server, getLibrary) {
|
|
|
21026
21146
|
}
|
|
21027
21147
|
);
|
|
21028
21148
|
}
|
|
21029
|
-
function registerCiteTool(server,
|
|
21149
|
+
function registerCiteTool(server, getLibraryOperations) {
|
|
21030
21150
|
server.registerTool(
|
|
21031
21151
|
"cite",
|
|
21032
21152
|
{
|
|
@@ -21038,8 +21158,8 @@ function registerCiteTool(server, getLibrary) {
|
|
|
21038
21158
|
}
|
|
21039
21159
|
},
|
|
21040
21160
|
async (args) => {
|
|
21041
|
-
const
|
|
21042
|
-
const result = await
|
|
21161
|
+
const libraryOps = getLibraryOperations();
|
|
21162
|
+
const result = await libraryOps.cite({
|
|
21043
21163
|
identifiers: args.ids,
|
|
21044
21164
|
style: args.style ?? "apa",
|
|
21045
21165
|
format: args.format ?? "text"
|
|
@@ -21053,7 +21173,7 @@ function registerCiteTool(server, getLibrary) {
|
|
|
21053
21173
|
}
|
|
21054
21174
|
);
|
|
21055
21175
|
}
|
|
21056
|
-
function registerFulltextAttachTool(server,
|
|
21176
|
+
function registerFulltextAttachTool(server, getLibraryOperations, getConfig) {
|
|
21057
21177
|
server.registerTool(
|
|
21058
21178
|
"fulltext_attach",
|
|
21059
21179
|
{
|
|
@@ -21064,9 +21184,9 @@ function registerFulltextAttachTool(server, getLibrary, getConfig) {
|
|
|
21064
21184
|
}
|
|
21065
21185
|
},
|
|
21066
21186
|
async (args) => {
|
|
21067
|
-
const
|
|
21187
|
+
const libraryOps = getLibraryOperations();
|
|
21068
21188
|
const config2 = getConfig();
|
|
21069
|
-
const result = await fulltextAttach(
|
|
21189
|
+
const result = await fulltextAttach(libraryOps, {
|
|
21070
21190
|
identifier: args.id,
|
|
21071
21191
|
filePath: args.path,
|
|
21072
21192
|
force: true,
|
|
@@ -21090,7 +21210,7 @@ function registerFulltextAttachTool(server, getLibrary, getConfig) {
|
|
|
21090
21210
|
}
|
|
21091
21211
|
);
|
|
21092
21212
|
}
|
|
21093
|
-
function registerFulltextGetTool(server,
|
|
21213
|
+
function registerFulltextGetTool(server, getLibraryOperations, getConfig) {
|
|
21094
21214
|
server.registerTool(
|
|
21095
21215
|
"fulltext_get",
|
|
21096
21216
|
{
|
|
@@ -21100,9 +21220,9 @@ function registerFulltextGetTool(server, getLibrary, getConfig) {
|
|
|
21100
21220
|
}
|
|
21101
21221
|
},
|
|
21102
21222
|
async (args) => {
|
|
21103
|
-
const
|
|
21223
|
+
const libraryOps = getLibraryOperations();
|
|
21104
21224
|
const config2 = getConfig();
|
|
21105
|
-
const pathResult = await fulltextGet(
|
|
21225
|
+
const pathResult = await fulltextGet(libraryOps, {
|
|
21106
21226
|
identifier: args.id,
|
|
21107
21227
|
fulltextDirectory: config2.fulltext.directory
|
|
21108
21228
|
});
|
|
@@ -21114,7 +21234,7 @@ function registerFulltextGetTool(server, getLibrary, getConfig) {
|
|
|
21114
21234
|
}
|
|
21115
21235
|
const responses = [];
|
|
21116
21236
|
if (pathResult.paths?.markdown) {
|
|
21117
|
-
const contentResult = await fulltextGet(
|
|
21237
|
+
const contentResult = await fulltextGet(libraryOps, {
|
|
21118
21238
|
identifier: args.id,
|
|
21119
21239
|
type: "markdown",
|
|
21120
21240
|
stdout: true,
|
|
@@ -21143,7 +21263,7 @@ function registerFulltextGetTool(server, getLibrary, getConfig) {
|
|
|
21143
21263
|
}
|
|
21144
21264
|
);
|
|
21145
21265
|
}
|
|
21146
|
-
function registerFulltextDetachTool(server,
|
|
21266
|
+
function registerFulltextDetachTool(server, getLibraryOperations, getConfig) {
|
|
21147
21267
|
server.registerTool(
|
|
21148
21268
|
"fulltext_detach",
|
|
21149
21269
|
{
|
|
@@ -21153,9 +21273,9 @@ function registerFulltextDetachTool(server, getLibrary, getConfig) {
|
|
|
21153
21273
|
}
|
|
21154
21274
|
},
|
|
21155
21275
|
async (args) => {
|
|
21156
|
-
const
|
|
21276
|
+
const libraryOps = getLibraryOperations();
|
|
21157
21277
|
const config2 = getConfig();
|
|
21158
|
-
const result = await fulltextDetach(
|
|
21278
|
+
const result = await fulltextDetach(libraryOps, {
|
|
21159
21279
|
identifier: args.id,
|
|
21160
21280
|
fulltextDirectory: config2.fulltext.directory
|
|
21161
21281
|
});
|
|
@@ -21177,30 +21297,46 @@ function registerFulltextDetachTool(server, getLibrary, getConfig) {
|
|
|
21177
21297
|
}
|
|
21178
21298
|
);
|
|
21179
21299
|
}
|
|
21180
|
-
function registerListTool(server,
|
|
21300
|
+
function registerListTool(server, getLibraryOperations, getConfig) {
|
|
21181
21301
|
server.registerTool(
|
|
21182
21302
|
"list",
|
|
21183
21303
|
{
|
|
21184
|
-
description: "List
|
|
21304
|
+
description: "List references in the library. Supports different output formats, sorting, and pagination.",
|
|
21185
21305
|
inputSchema: {
|
|
21186
|
-
format: z.enum(["json", "bibtex", "pretty"]).optional().describe("Output format: json, bibtex, or pretty (default: pretty)")
|
|
21306
|
+
format: z.enum(["json", "bibtex", "pretty"]).optional().describe("Output format: json, bibtex, or pretty (default: pretty)"),
|
|
21307
|
+
sort: sortFieldSchema.optional().describe("Sort by field: created, updated, published, author, title (default: updated)"),
|
|
21308
|
+
order: sortOrderSchema.optional().describe("Sort order: asc or desc (default: desc)"),
|
|
21309
|
+
limit: z.number().int().min(0).optional().describe("Maximum number of results (0 = no limit)"),
|
|
21310
|
+
offset: z.number().int().min(0).optional().describe("Number of results to skip (default: 0)")
|
|
21187
21311
|
}
|
|
21188
21312
|
},
|
|
21189
21313
|
async (args) => {
|
|
21190
|
-
const
|
|
21191
|
-
const
|
|
21192
|
-
|
|
21314
|
+
const libraryOps = getLibraryOperations();
|
|
21315
|
+
const config2 = getConfig();
|
|
21316
|
+
const limit2 = args.limit ?? config2.mcp.defaultLimit;
|
|
21317
|
+
const result = await libraryOps.list({
|
|
21318
|
+
format: args.format ?? "pretty",
|
|
21319
|
+
limit: limit2,
|
|
21320
|
+
...pickDefined(args, ["sort", "order", "offset"])
|
|
21193
21321
|
});
|
|
21194
21322
|
return {
|
|
21195
|
-
content:
|
|
21196
|
-
|
|
21197
|
-
|
|
21198
|
-
|
|
21323
|
+
content: [
|
|
21324
|
+
{
|
|
21325
|
+
type: "text",
|
|
21326
|
+
text: JSON.stringify({
|
|
21327
|
+
total: result.total,
|
|
21328
|
+
limit: result.limit,
|
|
21329
|
+
offset: result.offset,
|
|
21330
|
+
nextOffset: result.nextOffset,
|
|
21331
|
+
items: result.items
|
|
21332
|
+
})
|
|
21333
|
+
}
|
|
21334
|
+
]
|
|
21199
21335
|
};
|
|
21200
21336
|
}
|
|
21201
21337
|
);
|
|
21202
21338
|
}
|
|
21203
|
-
function registerRemoveTool(server,
|
|
21339
|
+
function registerRemoveTool(server, getLibraryOperations) {
|
|
21204
21340
|
server.registerTool(
|
|
21205
21341
|
"remove",
|
|
21206
21342
|
{
|
|
@@ -21221,8 +21357,8 @@ function registerRemoveTool(server, getLibrary) {
|
|
|
21221
21357
|
]
|
|
21222
21358
|
};
|
|
21223
21359
|
}
|
|
21224
|
-
const
|
|
21225
|
-
const result = await
|
|
21360
|
+
const libraryOps = getLibraryOperations();
|
|
21361
|
+
const result = await libraryOps.remove(args.id);
|
|
21226
21362
|
if (!result.removed) {
|
|
21227
21363
|
return {
|
|
21228
21364
|
content: [
|
|
@@ -21246,39 +21382,57 @@ Title: ${title2}`
|
|
|
21246
21382
|
}
|
|
21247
21383
|
);
|
|
21248
21384
|
}
|
|
21249
|
-
function registerSearchTool(server,
|
|
21385
|
+
function registerSearchTool(server, getLibraryOperations, getConfig) {
|
|
21250
21386
|
server.registerTool(
|
|
21251
21387
|
"search",
|
|
21252
21388
|
{
|
|
21253
|
-
description: "Search references in the library. Supports query syntax: author:name, year:YYYY, title:text, type:article-journal, tag:name, or free text for full-text search.",
|
|
21389
|
+
description: "Search references in the library. Supports query syntax: author:name, year:YYYY, title:text, type:article-journal, tag:name, or free text for full-text search. Supports sorting and pagination.",
|
|
21254
21390
|
inputSchema: {
|
|
21255
|
-
query: z.string().describe("Search query string")
|
|
21391
|
+
query: z.string().describe("Search query string"),
|
|
21392
|
+
sort: searchSortFieldSchema.optional().describe(
|
|
21393
|
+
"Sort by field: created, updated, published, author, title, relevance (default: updated)"
|
|
21394
|
+
),
|
|
21395
|
+
order: sortOrderSchema.optional().describe("Sort order: asc or desc (default: desc)"),
|
|
21396
|
+
limit: z.number().int().min(0).optional().describe("Maximum number of results (0 = no limit)"),
|
|
21397
|
+
offset: z.number().int().min(0).optional().describe("Number of results to skip (default: 0)")
|
|
21256
21398
|
}
|
|
21257
21399
|
},
|
|
21258
21400
|
async (args) => {
|
|
21259
|
-
const
|
|
21260
|
-
const
|
|
21401
|
+
const libraryOps = getLibraryOperations();
|
|
21402
|
+
const config2 = getConfig();
|
|
21403
|
+
const limit2 = args.limit ?? config2.mcp.defaultLimit;
|
|
21404
|
+
const result = await libraryOps.search({
|
|
21261
21405
|
query: args.query,
|
|
21262
|
-
format: "pretty"
|
|
21406
|
+
format: "pretty",
|
|
21407
|
+
limit: limit2,
|
|
21408
|
+
...pickDefined(args, ["sort", "order", "offset"])
|
|
21263
21409
|
});
|
|
21264
21410
|
return {
|
|
21265
|
-
content:
|
|
21266
|
-
|
|
21267
|
-
|
|
21268
|
-
|
|
21411
|
+
content: [
|
|
21412
|
+
{
|
|
21413
|
+
type: "text",
|
|
21414
|
+
text: JSON.stringify({
|
|
21415
|
+
total: result.total,
|
|
21416
|
+
limit: result.limit,
|
|
21417
|
+
offset: result.offset,
|
|
21418
|
+
nextOffset: result.nextOffset,
|
|
21419
|
+
items: result.items
|
|
21420
|
+
})
|
|
21421
|
+
}
|
|
21422
|
+
]
|
|
21269
21423
|
};
|
|
21270
21424
|
}
|
|
21271
21425
|
);
|
|
21272
21426
|
}
|
|
21273
|
-
function registerAllTools(server,
|
|
21274
|
-
registerSearchTool(server,
|
|
21275
|
-
registerListTool(server,
|
|
21276
|
-
registerCiteTool(server,
|
|
21277
|
-
registerAddTool(server,
|
|
21278
|
-
registerRemoveTool(server,
|
|
21279
|
-
registerFulltextAttachTool(server,
|
|
21280
|
-
registerFulltextGetTool(server,
|
|
21281
|
-
registerFulltextDetachTool(server,
|
|
21427
|
+
function registerAllTools(server, getLibraryOperations, getConfig) {
|
|
21428
|
+
registerSearchTool(server, getLibraryOperations, getConfig);
|
|
21429
|
+
registerListTool(server, getLibraryOperations, getConfig);
|
|
21430
|
+
registerCiteTool(server, getLibraryOperations);
|
|
21431
|
+
registerAddTool(server, getLibraryOperations);
|
|
21432
|
+
registerRemoveTool(server, getLibraryOperations);
|
|
21433
|
+
registerFulltextAttachTool(server, getLibraryOperations, getConfig);
|
|
21434
|
+
registerFulltextGetTool(server, getLibraryOperations, getConfig);
|
|
21435
|
+
registerFulltextDetachTool(server, getLibraryOperations, getConfig);
|
|
21282
21436
|
}
|
|
21283
21437
|
async function createMcpServer(options) {
|
|
21284
21438
|
const serverInfo = {
|
|
@@ -21293,10 +21447,10 @@ async function createMcpServer(options) {
|
|
|
21293
21447
|
}
|
|
21294
21448
|
const context = await createMcpContext(contextOptions);
|
|
21295
21449
|
const server = new McpServer(serverInfo);
|
|
21296
|
-
const
|
|
21450
|
+
const getLibraryOperations = () => context.libraryOperations;
|
|
21297
21451
|
const getConfig = () => context.config;
|
|
21298
|
-
registerAllTools(server,
|
|
21299
|
-
registerAllResources(server,
|
|
21452
|
+
registerAllTools(server, getLibraryOperations, getConfig);
|
|
21453
|
+
registerAllResources(server, getLibraryOperations);
|
|
21300
21454
|
const transport = new StdioServerTransport(
|
|
21301
21455
|
options.stdin ?? process.stdin,
|
|
21302
21456
|
options.stdout ?? process.stdout
|
|
@@ -21330,8 +21484,8 @@ async function mcpStart(options) {
|
|
|
21330
21484
|
return result;
|
|
21331
21485
|
}
|
|
21332
21486
|
async function executeRemove(options, context) {
|
|
21333
|
-
const { identifier,
|
|
21334
|
-
return context.library.remove(identifier, {
|
|
21487
|
+
const { identifier, idType = "id" } = options;
|
|
21488
|
+
return context.library.remove(identifier, { idType });
|
|
21335
21489
|
}
|
|
21336
21490
|
function formatRemoveOutput(result, identifier) {
|
|
21337
21491
|
if (!result.removed) {
|
|
@@ -21378,6 +21532,18 @@ async function deleteFulltextFiles(item, fulltextDirectory) {
|
|
|
21378
21532
|
}
|
|
21379
21533
|
}
|
|
21380
21534
|
}
|
|
21535
|
+
const VALID_SEARCH_SORT_FIELDS = /* @__PURE__ */ new Set([
|
|
21536
|
+
"created",
|
|
21537
|
+
"updated",
|
|
21538
|
+
"published",
|
|
21539
|
+
"author",
|
|
21540
|
+
"title",
|
|
21541
|
+
"relevance",
|
|
21542
|
+
"add",
|
|
21543
|
+
"mod",
|
|
21544
|
+
"pub",
|
|
21545
|
+
"rel"
|
|
21546
|
+
]);
|
|
21381
21547
|
function getSearchFormat(options) {
|
|
21382
21548
|
if (options.json) return "json";
|
|
21383
21549
|
if (options.idsOnly) return "ids-only";
|
|
@@ -21394,17 +21560,59 @@ function validateOptions(options) {
|
|
|
21394
21560
|
"Multiple output formats specified. Only one of --json, --ids-only, --uuid, --bibtex can be used."
|
|
21395
21561
|
);
|
|
21396
21562
|
}
|
|
21563
|
+
if (options.sort !== void 0) {
|
|
21564
|
+
const sortStr = String(options.sort);
|
|
21565
|
+
if (!VALID_SEARCH_SORT_FIELDS.has(sortStr)) {
|
|
21566
|
+
throw new Error(`Invalid sort field: ${sortStr}`);
|
|
21567
|
+
}
|
|
21568
|
+
}
|
|
21569
|
+
if (options.order !== void 0) {
|
|
21570
|
+
const result = sortOrderSchema.safeParse(options.order);
|
|
21571
|
+
if (!result.success) {
|
|
21572
|
+
throw new Error(`Invalid sort order: ${options.order}`);
|
|
21573
|
+
}
|
|
21574
|
+
}
|
|
21575
|
+
const paginationResult = paginationOptionsSchema.safeParse({
|
|
21576
|
+
limit: options.limit,
|
|
21577
|
+
offset: options.offset
|
|
21578
|
+
});
|
|
21579
|
+
if (!paginationResult.success) {
|
|
21580
|
+
const issue2 = paginationResult.error.issues[0];
|
|
21581
|
+
throw new Error(`Invalid pagination option: ${issue2?.message ?? "unknown error"}`);
|
|
21582
|
+
}
|
|
21397
21583
|
}
|
|
21398
21584
|
async function executeSearch(options, context) {
|
|
21399
21585
|
validateOptions(options);
|
|
21400
21586
|
const format2 = getSearchFormat(options);
|
|
21401
|
-
|
|
21587
|
+
const sort = options.sort ? resolveSortAlias(options.sort) : void 0;
|
|
21588
|
+
return context.library.search({
|
|
21589
|
+
query: options.query,
|
|
21590
|
+
format: format2,
|
|
21591
|
+
...sort !== void 0 && { sort },
|
|
21592
|
+
...pickDefined(options, ["order", "limit", "offset"])
|
|
21593
|
+
});
|
|
21402
21594
|
}
|
|
21403
|
-
function formatSearchOutput(result) {
|
|
21595
|
+
function formatSearchOutput(result, isJson = false) {
|
|
21596
|
+
if (isJson) {
|
|
21597
|
+
return JSON.stringify({
|
|
21598
|
+
items: result.items,
|
|
21599
|
+
total: result.total,
|
|
21600
|
+
limit: result.limit,
|
|
21601
|
+
offset: result.offset,
|
|
21602
|
+
nextOffset: result.nextOffset
|
|
21603
|
+
});
|
|
21604
|
+
}
|
|
21404
21605
|
if (result.items.length === 0) {
|
|
21405
21606
|
return "";
|
|
21406
21607
|
}
|
|
21407
|
-
|
|
21608
|
+
const lines = [];
|
|
21609
|
+
if (result.limit > 0 && result.total > 0) {
|
|
21610
|
+
const start = result.offset + 1;
|
|
21611
|
+
const end = result.offset + result.items.length;
|
|
21612
|
+
lines.push(`# Showing ${start}-${end} of ${result.total} references`);
|
|
21613
|
+
}
|
|
21614
|
+
lines.push(...result.items);
|
|
21615
|
+
return lines.join("\n");
|
|
21408
21616
|
}
|
|
21409
21617
|
async function serverStart(options) {
|
|
21410
21618
|
const existingStatus = await serverStatus(options.portfilePath);
|
|
@@ -21491,8 +21699,8 @@ async function serverStatus(portfilePath) {
|
|
|
21491
21699
|
return result;
|
|
21492
21700
|
}
|
|
21493
21701
|
async function executeUpdate(options, context) {
|
|
21494
|
-
const { identifier, updates,
|
|
21495
|
-
return context.library.update(identifier, updates, {
|
|
21702
|
+
const { identifier, updates, idType = "id" } = options;
|
|
21703
|
+
return context.library.update(identifier, updates, { idType });
|
|
21496
21704
|
}
|
|
21497
21705
|
function formatUpdateOutput(result, identifier) {
|
|
21498
21706
|
if (!result.updated) {
|
|
@@ -21513,47 +21721,6 @@ function formatUpdateOutput(result, identifier) {
|
|
|
21513
21721
|
}
|
|
21514
21722
|
return parts.join("\n");
|
|
21515
21723
|
}
|
|
21516
|
-
class OperationsLibrary {
|
|
21517
|
-
constructor(library) {
|
|
21518
|
-
this.library = library;
|
|
21519
|
-
}
|
|
21520
|
-
// ILibrary delegation
|
|
21521
|
-
find(identifier, options) {
|
|
21522
|
-
return this.library.find(identifier, options);
|
|
21523
|
-
}
|
|
21524
|
-
getAll() {
|
|
21525
|
-
return this.library.getAll();
|
|
21526
|
-
}
|
|
21527
|
-
add(item) {
|
|
21528
|
-
return this.library.add(item);
|
|
21529
|
-
}
|
|
21530
|
-
update(idOrUuid, updates, options) {
|
|
21531
|
-
return this.library.update(idOrUuid, updates, options);
|
|
21532
|
-
}
|
|
21533
|
-
remove(identifier, options) {
|
|
21534
|
-
return this.library.remove(identifier, options);
|
|
21535
|
-
}
|
|
21536
|
-
save() {
|
|
21537
|
-
return this.library.save();
|
|
21538
|
-
}
|
|
21539
|
-
// High-level operations
|
|
21540
|
-
async search(options) {
|
|
21541
|
-
const { searchReferences: searchReferences2 } = await import("./chunks/index-D4Q13N-R.js").then((n) => n.i);
|
|
21542
|
-
return searchReferences2(this.library, options);
|
|
21543
|
-
}
|
|
21544
|
-
async list(options) {
|
|
21545
|
-
const { listReferences: listReferences2 } = await import("./chunks/index-D4Q13N-R.js").then((n) => n.h);
|
|
21546
|
-
return listReferences2(this.library, options ?? {});
|
|
21547
|
-
}
|
|
21548
|
-
async cite(options) {
|
|
21549
|
-
const { citeReferences: citeReferences2 } = await import("./chunks/index-D4Q13N-R.js").then((n) => n.f);
|
|
21550
|
-
return citeReferences2(this.library, options);
|
|
21551
|
-
}
|
|
21552
|
-
async import(inputs, options) {
|
|
21553
|
-
const { addReferences: addReferences2 } = await import("./chunks/index-D4Q13N-R.js").then((n) => n.e);
|
|
21554
|
-
return addReferences2(inputs, this.library, options ?? {});
|
|
21555
|
-
}
|
|
21556
|
-
}
|
|
21557
21724
|
class ServerClient {
|
|
21558
21725
|
constructor(baseUrl) {
|
|
21559
21726
|
this.baseUrl = baseUrl;
|
|
@@ -21576,12 +21743,12 @@ class ServerClient {
|
|
|
21576
21743
|
/**
|
|
21577
21744
|
* Find reference by citation ID or UUID.
|
|
21578
21745
|
* @param identifier - Citation ID or UUID
|
|
21579
|
-
* @param options - Find options (
|
|
21746
|
+
* @param options - Find options (idType to specify identifier type)
|
|
21580
21747
|
* @returns CSL item or undefined if not found
|
|
21581
21748
|
*/
|
|
21582
21749
|
async find(identifier, options = {}) {
|
|
21583
|
-
const {
|
|
21584
|
-
const url =
|
|
21750
|
+
const { idType = "id" } = options;
|
|
21751
|
+
const url = idType === "uuid" ? `${this.baseUrl}/api/references/uuid/${encodeURIComponent(identifier)}` : `${this.baseUrl}/api/references/id/${encodeURIComponent(identifier)}`;
|
|
21585
21752
|
const response = await fetch(url);
|
|
21586
21753
|
if (response.status === 404) {
|
|
21587
21754
|
return void 0;
|
|
@@ -21615,12 +21782,12 @@ class ServerClient {
|
|
|
21615
21782
|
* Update reference by citation ID or UUID.
|
|
21616
21783
|
* @param identifier - Citation ID or UUID
|
|
21617
21784
|
* @param updates - Partial CSL item with fields to update
|
|
21618
|
-
* @param options - Update options (
|
|
21785
|
+
* @param options - Update options (idType to specify identifier type, onIdCollision for collision handling)
|
|
21619
21786
|
* @returns Update result with updated item, success status, and any ID changes
|
|
21620
21787
|
*/
|
|
21621
21788
|
async update(identifier, updates, options) {
|
|
21622
|
-
const {
|
|
21623
|
-
const url =
|
|
21789
|
+
const { idType = "id", onIdCollision } = options ?? {};
|
|
21790
|
+
const url = idType === "uuid" ? `${this.baseUrl}/api/references/uuid/${encodeURIComponent(identifier)}` : `${this.baseUrl}/api/references/id/${encodeURIComponent(identifier)}`;
|
|
21624
21791
|
const response = await fetch(url, {
|
|
21625
21792
|
method: "PUT",
|
|
21626
21793
|
headers: { "Content-Type": "application/json" },
|
|
@@ -21640,12 +21807,12 @@ class ServerClient {
|
|
|
21640
21807
|
/**
|
|
21641
21808
|
* Remove a reference by citation ID or UUID.
|
|
21642
21809
|
* @param identifier - The citation ID or UUID of the reference to remove
|
|
21643
|
-
* @param options - Remove options (
|
|
21810
|
+
* @param options - Remove options (idType to specify identifier type)
|
|
21644
21811
|
* @returns Remove result with removed status and removedItem
|
|
21645
21812
|
*/
|
|
21646
21813
|
async remove(identifier, options = {}) {
|
|
21647
|
-
const {
|
|
21648
|
-
const url =
|
|
21814
|
+
const { idType = "id" } = options;
|
|
21815
|
+
const url = idType === "uuid" ? `${this.baseUrl}/api/references/uuid/${encodeURIComponent(identifier)}` : `${this.baseUrl}/api/references/id/${encodeURIComponent(identifier)}`;
|
|
21649
21816
|
const response = await fetch(url, {
|
|
21650
21817
|
method: "DELETE"
|
|
21651
21818
|
});
|
|
@@ -21783,6 +21950,199 @@ async function waitForPortfile(timeoutMs) {
|
|
|
21783
21950
|
}
|
|
21784
21951
|
throw new Error(`Server failed to start: portfile not created within ${timeoutMs}ms`);
|
|
21785
21952
|
}
|
|
21953
|
+
const SEARCH_SORT_FIELDS = searchSortFieldSchema.options;
|
|
21954
|
+
const SORT_ORDERS = sortOrderSchema.options;
|
|
21955
|
+
const CITATION_FORMATS = ["text", "html", "rtf"];
|
|
21956
|
+
const LOG_LEVELS = ["silent", "info", "debug"];
|
|
21957
|
+
const OPTION_VALUES = {
|
|
21958
|
+
"--sort": SEARCH_SORT_FIELDS,
|
|
21959
|
+
// search includes 'relevance'
|
|
21960
|
+
"--order": SORT_ORDERS,
|
|
21961
|
+
"--format": CITATION_FORMATS,
|
|
21962
|
+
"--style": BUILTIN_STYLES,
|
|
21963
|
+
"--log-level": LOG_LEVELS
|
|
21964
|
+
};
|
|
21965
|
+
const ID_COMPLETION_COMMANDS = /* @__PURE__ */ new Set(["cite", "remove", "update"]);
|
|
21966
|
+
const ID_COMPLETION_FULLTEXT_SUBCOMMANDS = /* @__PURE__ */ new Set(["attach", "get", "detach"]);
|
|
21967
|
+
function toCompletionItems(values) {
|
|
21968
|
+
return values.map((name2) => ({ name: name2 }));
|
|
21969
|
+
}
|
|
21970
|
+
function extractSubcommands(program) {
|
|
21971
|
+
return program.commands.map((cmd) => ({
|
|
21972
|
+
name: cmd.name(),
|
|
21973
|
+
description: cmd.description()
|
|
21974
|
+
}));
|
|
21975
|
+
}
|
|
21976
|
+
function extractOptions(cmd) {
|
|
21977
|
+
const options = [];
|
|
21978
|
+
for (const opt of cmd.options) {
|
|
21979
|
+
const longFlag = opt.long;
|
|
21980
|
+
const shortFlag = opt.short;
|
|
21981
|
+
const description2 = opt.description;
|
|
21982
|
+
if (longFlag) {
|
|
21983
|
+
options.push({ name: longFlag, description: description2 });
|
|
21984
|
+
}
|
|
21985
|
+
if (shortFlag) {
|
|
21986
|
+
options.push({ name: shortFlag, description: description2 });
|
|
21987
|
+
}
|
|
21988
|
+
}
|
|
21989
|
+
return options;
|
|
21990
|
+
}
|
|
21991
|
+
function extractGlobalOptions(program) {
|
|
21992
|
+
const options = extractOptions(program);
|
|
21993
|
+
options.push({ name: "--help", description: "display help for command" });
|
|
21994
|
+
options.push({ name: "--version", description: "output the version number" });
|
|
21995
|
+
return options;
|
|
21996
|
+
}
|
|
21997
|
+
function findSubcommand(program, name2) {
|
|
21998
|
+
return program.commands.find((cmd) => cmd.name() === name2);
|
|
21999
|
+
}
|
|
22000
|
+
function getCompletions(env, program) {
|
|
22001
|
+
const { line, prev, last } = env;
|
|
22002
|
+
const words = line.trim().split(/\s+/);
|
|
22003
|
+
const args = words.slice(1);
|
|
22004
|
+
const subcommands = extractSubcommands(program);
|
|
22005
|
+
const globalOptions = extractGlobalOptions(program);
|
|
22006
|
+
if (args.length === 0) {
|
|
22007
|
+
return subcommands;
|
|
22008
|
+
}
|
|
22009
|
+
const firstArg = args[0] ?? "";
|
|
22010
|
+
if (prev?.startsWith("-")) {
|
|
22011
|
+
const optionValues = OPTION_VALUES[prev];
|
|
22012
|
+
if (optionValues) {
|
|
22013
|
+
return toCompletionItems(optionValues);
|
|
22014
|
+
}
|
|
22015
|
+
}
|
|
22016
|
+
if (last.startsWith("-")) {
|
|
22017
|
+
const subCmd = findSubcommand(program, firstArg);
|
|
22018
|
+
const commandOptions = subCmd ? extractOptions(subCmd) : [];
|
|
22019
|
+
return [...commandOptions, ...globalOptions];
|
|
22020
|
+
}
|
|
22021
|
+
const parentCmd = findSubcommand(program, firstArg);
|
|
22022
|
+
if (parentCmd && parentCmd.commands.length > 0) {
|
|
22023
|
+
const nestedSubcommands = extractSubcommands(parentCmd);
|
|
22024
|
+
if (args.length === 1 || args.length === 2 && !last.startsWith("-")) {
|
|
22025
|
+
return nestedSubcommands;
|
|
22026
|
+
}
|
|
22027
|
+
}
|
|
22028
|
+
if (args.length === 1) {
|
|
22029
|
+
return subcommands.filter((cmd) => cmd.name.startsWith(last));
|
|
22030
|
+
}
|
|
22031
|
+
return subcommands;
|
|
22032
|
+
}
|
|
22033
|
+
function needsIdCompletion(env) {
|
|
22034
|
+
const { line, prev } = env;
|
|
22035
|
+
const words = line.trim().split(/\s+/);
|
|
22036
|
+
const args = words.slice(1);
|
|
22037
|
+
if (args.length === 0) {
|
|
22038
|
+
return { needs: false };
|
|
22039
|
+
}
|
|
22040
|
+
const command = args[0] ?? "";
|
|
22041
|
+
if (prev?.startsWith("-")) {
|
|
22042
|
+
return { needs: false };
|
|
22043
|
+
}
|
|
22044
|
+
if (command === "fulltext" && args.length >= 2) {
|
|
22045
|
+
const subcommand = args[1] ?? "";
|
|
22046
|
+
if (ID_COMPLETION_FULLTEXT_SUBCOMMANDS.has(subcommand)) {
|
|
22047
|
+
return { needs: true, command, subcommand };
|
|
22048
|
+
}
|
|
22049
|
+
return { needs: false };
|
|
22050
|
+
}
|
|
22051
|
+
if (ID_COMPLETION_COMMANDS.has(command)) {
|
|
22052
|
+
return { needs: true, command };
|
|
22053
|
+
}
|
|
22054
|
+
return { needs: false };
|
|
22055
|
+
}
|
|
22056
|
+
const MAX_ID_COMPLETIONS = 100;
|
|
22057
|
+
async function getLibraryForCompletion() {
|
|
22058
|
+
try {
|
|
22059
|
+
const config2 = loadConfig();
|
|
22060
|
+
const server = await getServerConnection(config2.library, config2);
|
|
22061
|
+
if (server) {
|
|
22062
|
+
return new ServerClient(server.baseUrl);
|
|
22063
|
+
}
|
|
22064
|
+
return await Library.load(config2.library);
|
|
22065
|
+
} catch {
|
|
22066
|
+
return void 0;
|
|
22067
|
+
}
|
|
22068
|
+
}
|
|
22069
|
+
function truncateTitle(title2, maxLength = 40) {
|
|
22070
|
+
if (!title2) return "";
|
|
22071
|
+
if (title2.length <= maxLength) return title2;
|
|
22072
|
+
return `${title2.slice(0, maxLength - 3)}...`;
|
|
22073
|
+
}
|
|
22074
|
+
async function getIdCompletions(library, prefix) {
|
|
22075
|
+
try {
|
|
22076
|
+
const items2 = await library.getAll();
|
|
22077
|
+
const completions = [];
|
|
22078
|
+
for (const item of items2) {
|
|
22079
|
+
const id2 = item.id;
|
|
22080
|
+
if (!id2) continue;
|
|
22081
|
+
if (prefix && !id2.toLowerCase().startsWith(prefix.toLowerCase())) {
|
|
22082
|
+
continue;
|
|
22083
|
+
}
|
|
22084
|
+
completions.push({
|
|
22085
|
+
name: id2,
|
|
22086
|
+
description: truncateTitle(item.title)
|
|
22087
|
+
});
|
|
22088
|
+
if (completions.length >= MAX_ID_COMPLETIONS) {
|
|
22089
|
+
break;
|
|
22090
|
+
}
|
|
22091
|
+
}
|
|
22092
|
+
return completions;
|
|
22093
|
+
} catch {
|
|
22094
|
+
return [];
|
|
22095
|
+
}
|
|
22096
|
+
}
|
|
22097
|
+
async function installCompletion() {
|
|
22098
|
+
const tabtab = await import("tabtab");
|
|
22099
|
+
await tabtab.install({
|
|
22100
|
+
name: "ref",
|
|
22101
|
+
completer: "ref"
|
|
22102
|
+
});
|
|
22103
|
+
}
|
|
22104
|
+
async function uninstallCompletion() {
|
|
22105
|
+
const tabtab = await import("tabtab");
|
|
22106
|
+
await tabtab.uninstall({
|
|
22107
|
+
name: "ref"
|
|
22108
|
+
});
|
|
22109
|
+
}
|
|
22110
|
+
async function handleCompletion(program) {
|
|
22111
|
+
const tabtab = await import("tabtab");
|
|
22112
|
+
const env = tabtab.parseEnv(process.env);
|
|
22113
|
+
if (!env.complete) {
|
|
22114
|
+
return;
|
|
22115
|
+
}
|
|
22116
|
+
if (env.last.startsWith("-")) {
|
|
22117
|
+
const completions2 = getCompletions(env, program);
|
|
22118
|
+
tabtab.log(completions2);
|
|
22119
|
+
return;
|
|
22120
|
+
}
|
|
22121
|
+
const idContext = needsIdCompletion(env);
|
|
22122
|
+
if (idContext.needs) {
|
|
22123
|
+
const library = await getLibraryForCompletion();
|
|
22124
|
+
if (library) {
|
|
22125
|
+
const idCompletions = await getIdCompletions(library, env.last);
|
|
22126
|
+
tabtab.log(idCompletions);
|
|
22127
|
+
return;
|
|
22128
|
+
}
|
|
22129
|
+
}
|
|
22130
|
+
const completions = getCompletions(env, program);
|
|
22131
|
+
tabtab.log(completions);
|
|
22132
|
+
}
|
|
22133
|
+
function registerCompletionCommand(program) {
|
|
22134
|
+
program.command("completion").description("Install or uninstall shell completion").argument("[action]", "Action to perform (install or uninstall)", "install").action(async (action) => {
|
|
22135
|
+
if (action === "install") {
|
|
22136
|
+
await installCompletion();
|
|
22137
|
+
} else if (action === "uninstall") {
|
|
22138
|
+
await uninstallCompletion();
|
|
22139
|
+
} else {
|
|
22140
|
+
console.error(`Unknown action: ${action}`);
|
|
22141
|
+
console.error("Usage: ref completion [install|uninstall]");
|
|
22142
|
+
process.exit(1);
|
|
22143
|
+
}
|
|
22144
|
+
});
|
|
22145
|
+
}
|
|
21786
22146
|
async function createExecutionContext(config2, loadLibrary) {
|
|
21787
22147
|
const server = await getServerConnection(config2.library, config2);
|
|
21788
22148
|
if (server) {
|
|
@@ -21894,6 +22254,7 @@ function createProgram() {
|
|
|
21894
22254
|
registerServerCommand(program);
|
|
21895
22255
|
registerFulltextCommand(program);
|
|
21896
22256
|
registerMcpCommand(program);
|
|
22257
|
+
registerCompletionCommand(program);
|
|
21897
22258
|
return program;
|
|
21898
22259
|
}
|
|
21899
22260
|
async function handleListAction(options, program) {
|
|
@@ -21902,7 +22263,7 @@ async function handleListAction(options, program) {
|
|
|
21902
22263
|
const config2 = await loadConfigWithOverrides({ ...globalOpts, ...options });
|
|
21903
22264
|
const context = await createExecutionContext(config2, Library.load);
|
|
21904
22265
|
const result = await executeList(options, context);
|
|
21905
|
-
const output = formatListOutput(result);
|
|
22266
|
+
const output = formatListOutput(result, options.json ?? false);
|
|
21906
22267
|
if (output) {
|
|
21907
22268
|
process.stdout.write(`${output}
|
|
21908
22269
|
`);
|
|
@@ -21915,7 +22276,7 @@ async function handleListAction(options, program) {
|
|
|
21915
22276
|
}
|
|
21916
22277
|
}
|
|
21917
22278
|
function registerListCommand(program) {
|
|
21918
|
-
program.command("list").description("List all references in the library").option("--json", "Output in JSON format").option("--ids-only", "Output only citation keys").option("--uuid", "Output only UUIDs").option("--bibtex", "Output in BibTeX format").action(async (options) => {
|
|
22279
|
+
program.command("list").description("List all references in the library").option("--json", "Output in JSON format").option("--ids-only", "Output only citation keys").option("--uuid", "Output only UUIDs").option("--bibtex", "Output in BibTeX format").option("--sort <field>", "Sort by field: created|updated|published|author|title").option("--order <order>", "Sort order: asc|desc").option("-n, --limit <n>", "Maximum number of results", Number.parseInt).option("--offset <n>", "Number of results to skip", Number.parseInt).action(async (options) => {
|
|
21919
22280
|
await handleListAction(options, program);
|
|
21920
22281
|
});
|
|
21921
22282
|
}
|
|
@@ -21925,7 +22286,7 @@ async function handleSearchAction(query, options, program) {
|
|
|
21925
22286
|
const config2 = await loadConfigWithOverrides({ ...globalOpts, ...options });
|
|
21926
22287
|
const context = await createExecutionContext(config2, Library.load);
|
|
21927
22288
|
const result = await executeSearch({ ...options, query }, context);
|
|
21928
|
-
const output = formatSearchOutput(result);
|
|
22289
|
+
const output = formatSearchOutput(result, options.json ?? false);
|
|
21929
22290
|
if (output) {
|
|
21930
22291
|
process.stdout.write(`${output}
|
|
21931
22292
|
`);
|
|
@@ -21938,7 +22299,7 @@ async function handleSearchAction(query, options, program) {
|
|
|
21938
22299
|
}
|
|
21939
22300
|
}
|
|
21940
22301
|
function registerSearchCommand(program) {
|
|
21941
|
-
program.command("search").description("Search references").argument("<query>", "Search query").option("--json", "Output in JSON format").option("--ids-only", "Output only citation keys").option("--uuid", "Output only UUIDs").option("--bibtex", "Output in BibTeX format").action(async (query, options) => {
|
|
22302
|
+
program.command("search").description("Search references").argument("<query>", "Search query").option("--json", "Output in JSON format").option("--ids-only", "Output only citation keys").option("--uuid", "Output only UUIDs").option("--bibtex", "Output in BibTeX format").option("--sort <field>", "Sort by field: created|updated|published|author|title|relevance").option("--order <order>", "Sort order: asc|desc").option("-n, --limit <n>", "Maximum number of results", Number.parseInt).option("--offset <n>", "Number of results to skip", Number.parseInt).action(async (query, options) => {
|
|
21942
22303
|
await handleSearchAction(query, options, program);
|
|
21943
22304
|
});
|
|
21944
22305
|
}
|
|
@@ -21987,12 +22348,16 @@ async function handleAddAction(inputs, options, program) {
|
|
|
21987
22348
|
}
|
|
21988
22349
|
}
|
|
21989
22350
|
function registerAddCommand(program) {
|
|
21990
|
-
program.command("add").description("Add new reference(s) to the library").argument("[input...]", "File paths or identifiers (PMID/DOI), or use stdin").option("-f, --force", "Skip duplicate detection").option(
|
|
22351
|
+
program.command("add").description("Add new reference(s) to the library").argument("[input...]", "File paths or identifiers (PMID/DOI/ISBN), or use stdin").option("-f, --force", "Skip duplicate detection").option(
|
|
22352
|
+
"--format <format>",
|
|
22353
|
+
"Explicit input format: json|bibtex|ris|pmid|doi|isbn|auto",
|
|
22354
|
+
"auto"
|
|
22355
|
+
).option("--verbose", "Show detailed error information").action(async (inputs, options) => {
|
|
21991
22356
|
await handleAddAction(inputs, options, program);
|
|
21992
22357
|
});
|
|
21993
22358
|
}
|
|
21994
|
-
async function findReferenceToRemove(identifier,
|
|
21995
|
-
return context.library.find(identifier, {
|
|
22359
|
+
async function findReferenceToRemove(identifier, useUuid, context) {
|
|
22360
|
+
return context.library.find(identifier, { idType: useUuid ? "uuid" : "id" });
|
|
21996
22361
|
}
|
|
21997
22362
|
async function confirmRemoval(refToRemove, force, fulltextWarning) {
|
|
21998
22363
|
if (force || !isTTY()) {
|
|
@@ -22060,8 +22425,8 @@ async function handleRemoveAction(identifier, options, program) {
|
|
|
22060
22425
|
const removeOptions = {
|
|
22061
22426
|
identifier
|
|
22062
22427
|
};
|
|
22063
|
-
if (options.uuid
|
|
22064
|
-
removeOptions.
|
|
22428
|
+
if (options.uuid) {
|
|
22429
|
+
removeOptions.idType = "uuid";
|
|
22065
22430
|
}
|
|
22066
22431
|
const result = await executeRemove(removeOptions, context);
|
|
22067
22432
|
const output = formatRemoveOutput(result, identifier);
|
|
@@ -22115,8 +22480,8 @@ async function handleUpdateAction(identifier, file, options, program) {
|
|
|
22115
22480
|
identifier,
|
|
22116
22481
|
updates: validatedUpdates
|
|
22117
22482
|
};
|
|
22118
|
-
if (options.uuid
|
|
22119
|
-
updateOptions.
|
|
22483
|
+
if (options.uuid) {
|
|
22484
|
+
updateOptions.idType = "uuid";
|
|
22120
22485
|
}
|
|
22121
22486
|
const result = await executeUpdate(updateOptions, context);
|
|
22122
22487
|
const output = formatUpdateOutput(result, identifier);
|
|
@@ -22296,7 +22661,7 @@ async function handleFulltextAttachAction(identifier, filePathArg, options, prog
|
|
|
22296
22661
|
...type2 && { type: type2 },
|
|
22297
22662
|
...options.move && { move: options.move },
|
|
22298
22663
|
...options.force && { force: options.force },
|
|
22299
|
-
...options.uuid && {
|
|
22664
|
+
...options.uuid && { idType: "uuid" },
|
|
22300
22665
|
...stdinContent && { stdinContent }
|
|
22301
22666
|
};
|
|
22302
22667
|
const result = await executeFulltextAttach(attachOptions, context);
|
|
@@ -22321,7 +22686,7 @@ async function handleFulltextGetAction(identifier, options, program) {
|
|
|
22321
22686
|
...options.pdf && { type: "pdf" },
|
|
22322
22687
|
...options.markdown && { type: "markdown" },
|
|
22323
22688
|
...options.stdout && { stdout: options.stdout },
|
|
22324
|
-
...options.uuid && {
|
|
22689
|
+
...options.uuid && { idType: "uuid" }
|
|
22325
22690
|
};
|
|
22326
22691
|
const result = await executeFulltextGet(getOptions, context);
|
|
22327
22692
|
if (result.success && result.content && options.stdout) {
|
|
@@ -22355,7 +22720,7 @@ async function handleFulltextDetachAction(identifier, options, program) {
|
|
|
22355
22720
|
...options.markdown && { type: "markdown" },
|
|
22356
22721
|
...options.delete && { delete: options.delete },
|
|
22357
22722
|
...options.force && { force: options.force },
|
|
22358
|
-
...options.uuid && {
|
|
22723
|
+
...options.uuid && { idType: "uuid" }
|
|
22359
22724
|
};
|
|
22360
22725
|
const result = await executeFulltextDetach(detachOptions, context);
|
|
22361
22726
|
const output = formatFulltextDetachOutput(result);
|
|
@@ -22382,6 +22747,10 @@ function registerFulltextCommand(program) {
|
|
|
22382
22747
|
}
|
|
22383
22748
|
async function main(argv) {
|
|
22384
22749
|
const program = createProgram();
|
|
22750
|
+
if (process.env.COMP_LINE) {
|
|
22751
|
+
await handleCompletion(program);
|
|
22752
|
+
return;
|
|
22753
|
+
}
|
|
22385
22754
|
process.on("SIGINT", () => {
|
|
22386
22755
|
process.exit(130);
|
|
22387
22756
|
});
|