paper-manager 0.10.4 → 0.11.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 CHANGED
@@ -55,10 +55,10 @@ paper config list [--user] # List all config
55
55
 
56
56
  ```bash
57
57
  paper kb create <name> -d <desc> [-e <model-id>] [--user] # Create a knowledge base
58
- paper kb list [--all | --user] # List knowledge bases
59
- paper kb update <id> [-n <name>] [-d <desc>] # Update knowledge base metadata
60
- paper kb remove <id> # Remove a knowledge base
61
- paper kb query <id> <query-text> # Query a knowledge base
58
+ paper kb list [--all | --user] [--json] [--jq <expr>] # List knowledge bases
59
+ paper kb update <id> [-n <name>] [-d <desc>] # Update knowledge base metadata
60
+ paper kb remove <id> # Remove a knowledge base
61
+ paper kb query <id> <query-text> [--json] [--jq <expr>] # Query a knowledge base
62
62
  ```
63
63
 
64
64
  ### Literature (`paper lit`)
@@ -67,10 +67,10 @@ paper kb query <id> <query-text> # Query a knowledge base
67
67
  paper lit add <kb-id> <file-path> # Add a literature (auto-extracts PDF metadata)
68
68
  paper lit remove <kb-id> <id> # Remove a literature
69
69
  paper lit update <kb-id> <id> [opts] # Update literature metadata
70
- paper lit list <kb-id> # List literatures
71
- paper lit search <kb-id> [opts] # Search literatures by metadata (--title/--author/--keyword/--doi)
72
- paper lit show <kb-id> <id> # Show literature details
73
- paper lit note list <lit-id> # List notes
70
+ paper lit list <kb-id> [--json] [--jq <expr>] # List literatures
71
+ paper lit search <kb-id> [opts] [--json] [--jq <expr>] # Search literatures by metadata
72
+ paper lit show <kb-id> <id> [--json] [--jq <expr>] # Show literature details
73
+ paper lit note list <lit-id> [--json] [--jq <expr>] # List notes
74
74
  paper lit note set <lit-id> <k> <v> # Set a note
75
75
  paper lit note remove <lit-id> <key> # Remove a note
76
76
  ```
@@ -79,7 +79,7 @@ paper lit note remove <lit-id> <key> # Remove a note
79
79
 
80
80
  ```bash
81
81
  paper util doi2bib <doi> # Convert a DOI to BibTeX citation
82
- paper util pdf-meta <file> [--json] # Extract metadata from a PDF file
82
+ paper util pdf-meta <file> [--json] [--jq <expr>] # Extract metadata from a PDF file
83
83
  ```
84
84
 
85
85
  ## Configuration
@@ -9,6 +9,7 @@ import * as userLit from "../db/user/literatures.js";
9
9
  import { removeImageDir } from "../extractor/markdown.js";
10
10
  import { log } from "../logger.js";
11
11
  import { queryVectorStore } from "../vector-store/index.js";
12
+ import { outputJson } from "./output.js";
12
13
  function resolveKnowledgeBase(id) {
13
14
  const pkb = projectKb.getKnowledgeBase(id);
14
15
  if (pkb)
@@ -59,6 +60,7 @@ export function createKnowledgeBaseCommand() {
59
60
  .option("--user", "List user knowledge bases only")
60
61
  .option("--all", "List all knowledge bases (default)")
61
62
  .option("--json", "Output as JSON")
63
+ .option("--jq <expression>", "Filter JSON output with a jq expression (implies --json)")
62
64
  .action((options) => {
63
65
  let results = [];
64
66
  if (options.user) {
@@ -70,16 +72,16 @@ export function createKnowledgeBaseCommand() {
70
72
  results = [...projectKbs, ...userKbs];
71
73
  }
72
74
  if (results.length === 0) {
73
- if (options.json) {
74
- log.plain("[]");
75
+ if (options.json || options.jq) {
76
+ outputJson([], options.jq);
75
77
  }
76
78
  else {
77
79
  log.info("No knowledge bases found.");
78
80
  }
79
81
  return;
80
82
  }
81
- if (options.json) {
82
- log.plain(JSON.stringify(results, null, 2));
83
+ if (options.json || options.jq) {
84
+ outputJson(results, options.jq);
83
85
  return;
84
86
  }
85
87
  for (const kb of results) {
@@ -164,6 +166,7 @@ export function createKnowledgeBaseCommand() {
164
166
  .description("Query a knowledge base")
165
167
  .option("-k, --top-k <number>", "Number of results", "5")
166
168
  .option("--json", "Output as JSON")
169
+ .option("--jq <expression>", "Filter JSON output with a jq expression (implies --json)")
167
170
  .action(async (id, queryText, options) => {
168
171
  const resolved = resolveKnowledgeBase(id);
169
172
  if (!resolved) {
@@ -181,19 +184,19 @@ export function createKnowledgeBaseCommand() {
181
184
  const k = parseInt(options.topK, 10);
182
185
  const results = await queryVectorStore(modelConfig, vectorDir, queryText, k);
183
186
  if (results.length === 0) {
184
- if (options.json) {
185
- log.plain("[]");
187
+ if (options.json || options.jq) {
188
+ outputJson([], options.jq);
186
189
  }
187
190
  else {
188
191
  log.info("No results found.");
189
192
  }
190
193
  return;
191
194
  }
192
- if (options.json) {
195
+ if (options.json || options.jq) {
193
196
  const output = results
194
197
  .filter((doc) => doc != null)
195
198
  .map((doc) => ({ pageContent: doc.pageContent, metadata: doc.metadata }));
196
- log.plain(JSON.stringify(output, null, 2));
199
+ outputJson(output, options.jq);
197
200
  return;
198
201
  }
199
202
  for (let i = 0; i < results.length; i++) {
@@ -13,6 +13,7 @@ import { convertPdfToMarkdown, isOpendataLoaderAvailable, removeImageDir, saveCo
13
13
  import { log } from "../logger.js";
14
14
  import { splitDocuments } from "../text-splitter.js";
15
15
  import { addDocuments, createVectorStore } from "../vector-store/index.js";
16
+ import { outputJson } from "./output.js";
16
17
  function resolveKnowledgeBase(id) {
17
18
  const pkb = projectKb.getKnowledgeBase(id);
18
19
  if (pkb)
@@ -249,6 +250,7 @@ export function createLiteratureCommand() {
249
250
  .command("list <knowledge-base-id>")
250
251
  .description("List literatures in a knowledge base")
251
252
  .option("--json", "Output as JSON")
253
+ .option("--jq <expression>", "Filter JSON output with a jq expression (implies --json)")
252
254
  .action((kbId, options) => {
253
255
  const resolved = resolveKnowledgeBase(kbId);
254
256
  if (!resolved) {
@@ -258,16 +260,16 @@ export function createLiteratureCommand() {
258
260
  const litOps = getLitOps(resolved.scope);
259
261
  const literatures = litOps.listLiteratures(kbId);
260
262
  if (literatures.length === 0) {
261
- if (options.json) {
262
- log.plain("[]");
263
+ if (options.json || options.jq) {
264
+ outputJson([], options.jq);
263
265
  }
264
266
  else {
265
267
  log.info("No literatures found.");
266
268
  }
267
269
  return;
268
270
  }
269
- if (options.json) {
270
- log.plain(JSON.stringify(literatures, null, 2));
271
+ if (options.json || options.jq) {
272
+ outputJson(literatures, options.jq);
271
273
  return;
272
274
  }
273
275
  const filesDir = getFilesDir(getBaseDir(resolved.scope));
@@ -292,6 +294,7 @@ export function createLiteratureCommand() {
292
294
  .option("-k, --keyword <keyword>", "Keyword substring")
293
295
  .option("--doi <doi>", "DOI substring")
294
296
  .option("--json", "Output as JSON")
297
+ .option("--jq <expression>", "Filter JSON output with a jq expression (implies --json)")
295
298
  .action((kbId, options) => {
296
299
  const resolved = resolveKnowledgeBase(kbId);
297
300
  if (!resolved) {
@@ -313,16 +316,16 @@ export function createLiteratureCommand() {
313
316
  doi: options.doi,
314
317
  });
315
318
  if (results.length === 0) {
316
- if (options.json) {
317
- log.plain("[]");
319
+ if (options.json || options.jq) {
320
+ outputJson([], options.jq);
318
321
  }
319
322
  else {
320
323
  log.info("No literatures found.");
321
324
  }
322
325
  return;
323
326
  }
324
- if (options.json) {
325
- log.plain(JSON.stringify(results, null, 2));
327
+ if (options.json || options.jq) {
328
+ outputJson(results, options.jq);
326
329
  return;
327
330
  }
328
331
  for (const l of results) {
@@ -344,6 +347,7 @@ export function createLiteratureCommand() {
344
347
  .command("show <knowledge-base-id> <id>")
345
348
  .description("Show literature details")
346
349
  .option("--json", "Output as JSON")
350
+ .option("--jq <expression>", "Filter JSON output with a jq expression (implies --json)")
347
351
  .action((kbId, id, options) => {
348
352
  const resolved = resolveKnowledgeBase(kbId);
349
353
  if (!resolved) {
@@ -356,8 +360,8 @@ export function createLiteratureCommand() {
356
360
  log.error(`Literature not found: ${id}`);
357
361
  process.exit(1);
358
362
  }
359
- if (options.json) {
360
- log.plain(JSON.stringify(literature, null, 2));
363
+ if (options.json || options.jq) {
364
+ outputJson(literature, options.jq);
361
365
  return;
362
366
  }
363
367
  const filesDir = getFilesDir(getBaseDir(resolved.scope));
@@ -369,14 +373,15 @@ export function createLiteratureCommand() {
369
373
  .command("list <literature-id>")
370
374
  .description("List all notes for a literature")
371
375
  .option("--json", "Output as JSON")
376
+ .option("--jq <expression>", "Filter JSON output with a jq expression (implies --json)")
372
377
  .action((litId, options) => {
373
378
  const literature = findLiterature(litId);
374
379
  if (!literature) {
375
380
  log.error(`Literature not found: ${litId}`);
376
381
  process.exit(1);
377
382
  }
378
- if (options.json) {
379
- log.plain(JSON.stringify(literature.notes, null, 2));
383
+ if (options.json || options.jq) {
384
+ outputJson(literature.notes, options.jq);
380
385
  return;
381
386
  }
382
387
  const entries = Object.entries(literature.notes);
@@ -0,0 +1,25 @@
1
+ import { jq } from "@eurfelux/jq-js";
2
+ import { log } from "../logger.js";
3
+ /**
4
+ * Output data as JSON, optionally filtered by a jq expression.
5
+ * When `jqExpr` is provided, applies the jq filter and prints each result value.
6
+ * Otherwise prints pretty-printed JSON.
7
+ */
8
+ export function outputJson(data, jqExpr) {
9
+ const jsonStr = JSON.stringify(data, null, 2);
10
+ if (jqExpr) {
11
+ const normalized = JSON.parse(jsonStr);
12
+ const results = jq(jqExpr, normalized);
13
+ for (const result of results) {
14
+ if (typeof result === "string") {
15
+ log.plain(result);
16
+ }
17
+ else {
18
+ log.plain(JSON.stringify(result, null, 2));
19
+ }
20
+ }
21
+ }
22
+ else {
23
+ log.plain(jsonStr);
24
+ }
25
+ }
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { Command } from "commander";
4
4
  import { extractPdfMetadata } from "../extractor/pdf.js";
5
5
  import { log } from "../logger.js";
6
+ import { outputJson } from "./output.js";
6
7
  export function createUtilCommand() {
7
8
  const util = new Command("util").description("Utility commands");
8
9
  util
@@ -26,6 +27,7 @@ export function createUtilCommand() {
26
27
  .command("pdf-meta <file>")
27
28
  .description("Extract metadata from a PDF file")
28
29
  .option("--json", "Output as JSON")
30
+ .option("--jq <expression>", "Filter JSON output with a jq expression (implies --json)")
29
31
  .action(async (file, options) => {
30
32
  const absolutePath = path.resolve(file);
31
33
  if (!existsSync(absolutePath)) {
@@ -44,8 +46,8 @@ export function createUtilCommand() {
44
46
  log.error(`Failed to parse PDF: ${err instanceof Error ? err.message : String(err)}`);
45
47
  process.exit(1);
46
48
  }
47
- if (options.json) {
48
- log.plain(JSON.stringify(meta, null, 2));
49
+ if (options.json || options.jq) {
50
+ outputJson(meta, options.jq);
49
51
  return;
50
52
  }
51
53
  log.header("PDF Metadata");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paper-manager",
3
- "version": "0.10.4",
3
+ "version": "0.11.0",
4
4
  "description": "A paper management system.",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/EurFelux/paper-manager",
@@ -21,6 +21,7 @@
21
21
  "dependencies": {
22
22
  "@ai-sdk/openai": "^3.0.37",
23
23
  "@ai-sdk/provider": "^3.0.8",
24
+ "@eurfelux/jq-js": "^0.3.0",
24
25
  "ai": "^6.0.105",
25
26
  "better-sqlite3": "^12.6.2",
26
27
  "chalk": "^5.6.2",