Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)
Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
170 lines
4.9 KiB
Python
170 lines
4.9 KiB
Python
"""Click-based CLI for the NoteGraph ingestion pipeline.
|
|
|
|
Subcommands:
|
|
import - Import files or directories into NoteGraph
|
|
watch - Start drop-folder watcher for continuous ingestion
|
|
sync - One-shot Nextcloud inbox sync
|
|
upload - Start web upload server
|
|
"""
|
|
|
|
import logging
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import click
|
|
|
|
from ingestion.config import IngestionConfig
|
|
|
|
|
|
def _setup_logging(verbose: bool) -> None:
|
|
"""Configure logging based on verbosity."""
|
|
level = logging.DEBUG if verbose else logging.INFO
|
|
logging.basicConfig(
|
|
level=level,
|
|
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
datefmt="%H:%M:%S",
|
|
)
|
|
|
|
|
|
@click.group()
|
|
@click.version_option(version="0.1.0", prog_name="notegraph-ingest")
|
|
def cli():
|
|
"""NoteGraph Ingestion — import documents into your knowledge base."""
|
|
pass
|
|
|
|
|
|
@cli.command("import")
|
|
@click.argument("path", type=click.Path(exists=True))
|
|
@click.option("--output-dir", "-o", type=click.Path(), default=None, help="Override output directory")
|
|
@click.option("--create-tasks", is_flag=True, help="Create OrgMyLife tasks for action items")
|
|
@click.option("--provider", type=str, default=None, help="Override LLM provider (openai, anthropic, etc.)")
|
|
@click.option("--dry-run", is_flag=True, help="Show what would be created without writing files")
|
|
@click.option("--no-commit", is_flag=True, help="Skip git auto-commit")
|
|
@click.option("--verbose", "-v", is_flag=True, help="Enable debug logging")
|
|
def import_cmd(path, output_dir, create_tasks, provider, dry_run, no_commit, verbose):
|
|
"""Import files or directories into NoteGraph.
|
|
|
|
PATH can be a single file or a directory to scan recursively.
|
|
"""
|
|
_setup_logging(verbose)
|
|
|
|
config = IngestionConfig()
|
|
if provider:
|
|
config.llm_provider = provider
|
|
|
|
config.validate_api_key()
|
|
|
|
from ingestion.discovery import discover_files
|
|
from ingestion.pipeline import process_batch
|
|
|
|
source_path = Path(path)
|
|
files = discover_files(source_path)
|
|
|
|
if not files:
|
|
click.echo("No supported files found.")
|
|
sys.exit(0)
|
|
|
|
click.echo(f"Found {len(files)} file(s) to process.")
|
|
|
|
if dry_run:
|
|
click.echo("[DRY RUN] No files will be written.\n")
|
|
|
|
def progress(current, total, filename):
|
|
click.echo(f" [{current}/{total}] {filename}")
|
|
|
|
result = process_batch(
|
|
paths=files,
|
|
config=config,
|
|
dry_run=dry_run,
|
|
no_commit=no_commit,
|
|
create_tasks=create_tasks,
|
|
output_dir=Path(output_dir) if output_dir else None,
|
|
progress_callback=progress,
|
|
)
|
|
|
|
# Summary
|
|
click.echo("")
|
|
click.echo(f"Done! Processed {result.total} file(s):")
|
|
click.echo(f" ✓ Success: {result.success}")
|
|
if result.failed:
|
|
click.echo(f" ✗ Failed: {result.failed}")
|
|
|
|
|
|
@cli.command("watch")
|
|
@click.option("--verbose", "-v", is_flag=True, help="Enable debug logging")
|
|
def watch_cmd(verbose):
|
|
"""Start drop-folder watcher for continuous ingestion."""
|
|
_setup_logging(verbose)
|
|
|
|
from ingestion.watcher import start_watcher
|
|
|
|
config = IngestionConfig()
|
|
config.validate_api_key()
|
|
|
|
click.echo(f"Watching inbox: {config.inbox_dir}")
|
|
click.echo("Press Ctrl+C to stop.\n")
|
|
|
|
try:
|
|
start_watcher(config)
|
|
except KeyboardInterrupt:
|
|
click.echo("\nWatcher stopped.")
|
|
|
|
|
|
@cli.command("sync")
|
|
@click.option("--verbose", "-v", is_flag=True, help="Enable debug logging")
|
|
def sync_cmd(verbose):
|
|
"""One-shot Nextcloud inbox sync."""
|
|
_setup_logging(verbose)
|
|
|
|
from ingestion.integrations.nextcloud import NextcloudInboxSync
|
|
|
|
config = IngestionConfig()
|
|
|
|
if not config.nextcloud_inbox_url:
|
|
click.echo("ERROR: NEXTCLOUD_INBOX_URL not configured.")
|
|
sys.exit(1)
|
|
|
|
config.validate_api_key()
|
|
|
|
sync = NextcloudInboxSync(config)
|
|
downloaded = sync.poll()
|
|
|
|
if not downloaded:
|
|
click.echo("No new files in Nextcloud inbox.")
|
|
return
|
|
|
|
click.echo(f"Downloaded {len(downloaded)} file(s) from Nextcloud.")
|
|
|
|
# Process downloaded files
|
|
from ingestion.pipeline import process_batch
|
|
|
|
result = process_batch(
|
|
paths=downloaded,
|
|
config=config,
|
|
)
|
|
|
|
# Mark processed files on Nextcloud
|
|
for r in result.results:
|
|
if r.success:
|
|
sync.mark_processed(r.source_file.name)
|
|
|
|
click.echo(f"Processed: {result.success} success, {result.failed} failed.")
|
|
|
|
|
|
@cli.command("upload")
|
|
@click.option("--host", default="0.0.0.0", help="Bind host")
|
|
@click.option("--port", default=8001, type=int, help="Bind port")
|
|
@click.option("--verbose", "-v", is_flag=True, help="Enable debug logging")
|
|
def upload_cmd(host, port, verbose):
|
|
"""Start web upload server for mobile file ingestion."""
|
|
_setup_logging(verbose)
|
|
|
|
import uvicorn
|
|
|
|
click.echo(f"Starting upload server on {host}:{port}")
|
|
uvicorn.run("ingestion.web.upload:app", host=host, port=port, log_level="info")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
cli()
|