Skip to content

CLI Reference

obsidian_import.cli

CLI for obsidian-import.

main

main() -> None

Extract files into Obsidian-flavored Markdown.

Source code in obsidian_import/cli.py
@click.group()
def main() -> None:
    """Extract files into Obsidian-flavored Markdown."""

convert

convert(path: str, output_path: str | None, config_path: str | None) -> None

Extract a single file to Obsidian markdown.

Source code in obsidian_import/cli.py
@main.command()
@click.argument("path", type=click.Path(exists=True))
@click.option("--output", "output_path", type=click.Path(), help="Output file path (default: stdout)")
@click.option("--config", "config_path", type=click.Path(exists=True), help="Config YAML file")
def convert(path: str, output_path: str | None, config_path: str | None) -> None:
    """Extract a single file to Obsidian markdown."""
    config = _resolve_config(config_path)
    source = Path(path)

    doc = extract_file(source, config)
    formatted = format_output(doc, config.output)

    if output_path:
        out = Path(output_path)
        out.parent.mkdir(parents=True, exist_ok=True)
        out.write_text(formatted, encoding="utf-8")
        _copy_associated_files(doc.associated_files, out.parent)
        if doc.media_files:
            media_dir = media_dir_for(source, out.parent)
            copy_media_files(doc.media_files, media_dir)
        click.echo(f"Extracted: {source} -> {out}")
    else:
        if doc.media_files:
            log.warning(
                "Media files extracted but no output directory specified; "
                "%d media files cannot be copied to stdout mode",
                len(doc.media_files),
            )
        click.echo(formatted)

discover

discover(config_path: str) -> None

List files matching configured input directories and extensions.

Source code in obsidian_import/cli.py
@main.command()
@click.option("--config", "config_path", required=True, type=click.Path(exists=True), help="Config YAML file")
def discover(config_path: str) -> None:
    """List files matching configured input directories and extensions."""
    config = load_config(Path(config_path))

    count = 0
    for f in discover_files(config):
        size_kb = f.size_bytes / 1024
        click.echo(f"  {f.extension:6s}  {size_kb:8.1f} KB  {f.path}")
        count += 1

    click.echo(f"\n{count} files found.")

batch

batch(config_path: str, output_dir: str | None) -> None

Extract all discovered files to Obsidian markdown.

Source code in obsidian_import/cli.py
@main.command()
@click.option("--config", "config_path", required=True, type=click.Path(exists=True), help="Config YAML file")
@click.option("--output-dir", type=click.Path(), help="Override output directory from config")
def batch(config_path: str, output_dir: str | None) -> None:
    """Extract all discovered files to Obsidian markdown."""
    config = load_config(Path(config_path))

    target_dir = output_dir if output_dir else config.output.directory
    out_dir = Path(target_dir)
    out_dir.mkdir(parents=True, exist_ok=True)

    success = 0
    failed = 0
    passthrough_count = 0

    for discovered in discover_files(config):
        if matches_passthrough(discovered.path, config.passthrough):
            try:
                dest = copy_passthrough(discovered.path, out_dir)
                click.echo(f"  COPY  {discovered.path} -> {dest}")
                passthrough_count += 1
            except ObsidianImportError as exc:
                click.echo(f"  FAIL  {discovered.path}: {exc}", err=True)
                failed += 1
            continue

        try:
            doc = extract_file(discovered.path, config)
            formatted = format_output(doc, config.output)
            source_root = Path(discovered.source_directory)
            out_path = output_path_for(discovered.path, target_dir, source_root)
            out_path.parent.mkdir(parents=True, exist_ok=True)
            out_path.write_text(formatted, encoding="utf-8")
            _copy_associated_files(doc.associated_files, out_path.parent)
            if doc.media_files:
                media_dir = media_dir_for(discovered.path, out_path.parent)
                copy_media_files(doc.media_files, media_dir)
            click.echo(f"  OK  {discovered.path} -> {out_path}")
            success += 1
        except ObsidianImportError as exc:
            click.echo(f"  FAIL  {discovered.path}: {exc}", err=True)
            failed += 1

    click.echo(f"\nDone: {success} extracted, {passthrough_count} copied, {failed} failed.")

doctor

doctor() -> None

Check backend availability.

Source code in obsidian_import/cli.py
@main.command()
def doctor() -> None:
    """Check backend availability."""
    checks = [
        ("native (pdf)", "native", ".pdf"),
        ("native (docx)", "native", ".docx"),
        ("native (pptx)", "native", ".pptx"),
        ("native (xlsx)", "native", ".xlsx"),
        ("native (csv)", "native", ".csv"),
        ("native (json)", "native", ".json"),
        ("native (yaml)", "native", ".yaml"),
        ("native (image)", "native", ".png"),
        ("markitdown", "markitdown", ".pdf"),
        ("docling", "docling", ".pdf"),
    ]

    all_ok = True
    for label, backend, ext in checks:
        available, message = check_backend_available(backend, ext)
        status = "OK" if available else "MISSING"
        click.echo(f"  {label:20s}  [{status}]  {message}")
        if not available and backend == "native":
            all_ok = False

    tools = {
        "pdfplumber": "PDF text extraction",
        "defusedxml": "DOCX XML parsing (secure)",
        "pptx": "PPTX extraction (python-pptx)",
        "openpyxl": "XLSX extraction",
    }

    click.echo("\nPython packages:")
    for pkg, purpose in tools.items():
        try:
            __import__(pkg)
            path = shutil.which(pkg) or "(importable)"
            click.echo(f"  {pkg:20s}  {path}")
        except ImportError:
            click.echo(f"  {pkg:20s}  MISSING — {purpose}")
            all_ok = False

    if all_ok:
        click.echo("\nAll required backends available.")
    else:
        click.echo("\nSome required backends are missing.")
        raise SystemExit(1)