#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Timestamp: 2026-01-27
# File: src/scitex_writer/_cli/bib.py
"""Bibliography CLI commands."""
import argparse
import sys
from pathlib import Path
def cmd_list_files(args: argparse.Namespace) -> int:
"""List bibliography files."""
from .. import bib
result = bib.list_files(args.project)
if not result["success"]:
print(f"Error: {result['error']}", file=sys.stderr)
return 1
print(f"# Bibliography Files ({result['count']})\n")
print("| File | Entries | Merged |")
print("|------|---------|--------|")
for f in result["bibfiles"]:
merged = "✓" if f["is_merged"] else ""
print(f"| {f['name']} | {f['entry_count']} | {merged} |")
return 0
def cmd_list_entries(args: argparse.Namespace) -> int:
"""List bibliography entries."""
from .. import bib
result = bib.list_entries(args.project, args.file)
if not result["success"]:
print(f"Error: {result['error']}", file=sys.stderr)
return 1
print(f"# Bibliography Entries ({result['count']})\n")
print("| Key | Type | File |")
print("|-----|------|------|")
for e in result["entries"]:
print(f"| {e['citation_key']} | {e['entry_type']} | {e['bibfile']} |")
return 0
def cmd_get(args: argparse.Namespace) -> int:
"""Get a specific bibliography entry."""
from .. import bib
result = bib.get(args.project, args.key)
if not result["success"]:
print(f"Error: {result['error']}", file=sys.stderr)
return 1
print(result["entry"])
return 0
def cmd_add(args: argparse.Namespace) -> int:
"""Add a bibliography entry."""
from .. import bib
if args.entry == "-":
entry = sys.stdin.read()
elif args.entry.startswith("@"):
entry = args.entry
else:
entry_path = Path(args.entry)
if entry_path.exists():
entry = entry_path.read_text(encoding="utf-8")
else:
entry = args.entry
result = bib.add(args.project, entry, args.file, not args.allow_duplicates)
if not result["success"]:
print(f"Error: {result['error']}", file=sys.stderr)
return 1
print(f"Added: {result['citation_key']} to {result['bibfile']}")
return 0
def cmd_remove(args: argparse.Namespace) -> int:
"""Remove a bibliography entry."""
from .. import bib
result = bib.remove(args.project, args.key)
if not result["success"]:
print(f"Error: {result['error']}", file=sys.stderr)
return 1
print(f"Removed: {result['citation_key']} from {result['removed_from']}")
return 0
def cmd_merge(args: argparse.Namespace) -> int:
"""Merge all bibliography files."""
from .. import bib
result = bib.merge(args.project, args.output, not args.keep_duplicates)
if not result["success"]:
print(f"Error: {result['error']}", file=sys.stderr)
return 1
print(f"Merged {result['entry_count']} entries to {result['output_file']}")
if result["duplicates_skipped"] > 0:
print(f"Skipped {result['duplicates_skipped']} duplicates")
return 0
def register_parser(subparsers) -> argparse.ArgumentParser:
"""Register bib subcommand parser."""
bib_help = """Bibliography management.
Quick start:
scitex-writer bib list-files # List .bib files
scitex-writer bib list-entries # List all entries
scitex-writer bib get Smith2024 # Get specific entry
scitex-writer bib add '@article{...}' # Add entry
scitex-writer bib remove Smith2024 # Remove entry
scitex-writer bib merge # Merge and deduplicate
"""
parser = subparsers.add_parser(
"bib",
help="Bibliography management",
description=bib_help,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
sub = parser.add_subparsers(dest="bib_command", title="Commands")
# list-files
lf = sub.add_parser("list-files", help="List .bib files")
lf.add_argument("-p", "--project", default=".", help="Project path")
lf.set_defaults(func=cmd_list_files)
# list-entries
le = sub.add_parser("list-entries", help="List bibliography entries")
le.add_argument("-p", "--project", default=".", help="Project path")
le.add_argument("-f", "--file", help="Specific .bib file")
le.set_defaults(func=cmd_list_entries)
# get
g = sub.add_parser("get", help="Get a specific entry")
g.add_argument("key", help="Citation key")
g.add_argument("-p", "--project", default=".", help="Project path")
g.set_defaults(func=cmd_get)
# add
a = sub.add_parser("add", help="Add a bibliography entry")
a.add_argument("entry", help="BibTeX entry, file path, or '-' for stdin")
a.add_argument("-p", "--project", default=".", help="Project path")
a.add_argument("-f", "--file", default="custom.bib", help="Target .bib file")
a.add_argument(
"--allow-duplicates", action="store_true", help="Allow duplicate keys"
)
a.set_defaults(func=cmd_add)
# remove
r = sub.add_parser("remove", help="Remove an entry")
r.add_argument("key", help="Citation key to remove")
r.add_argument("-p", "--project", default=".", help="Project path")
r.set_defaults(func=cmd_remove)
# merge
m = sub.add_parser("merge", help="Merge all .bib files")
m.add_argument("-p", "--project", default=".", help="Project path")
m.add_argument("-o", "--output", default="bibliography.bib", help="Output file")
m.add_argument(
"--keep-duplicates", action="store_true", help="Keep duplicate entries"
)
m.set_defaults(func=cmd_merge)
return parser
# EOF