Files
HCFS/hcfs-python/hcfs/cli.py
2025-07-30 09:34:16 +10:00

164 lines
5.2 KiB
Python

"""
HCFS Command Line Interface
"""
import asyncio
import signal
import sys
from pathlib import Path
from typing import Optional
import click
import pyfuse3
import uvicorn
from .core.context_db import ContextDatabase, Context
from .core.filesystem import HCFSFilesystem
from .core.embeddings import EmbeddingManager
from .api.server import create_app
@click.group()
def main():
"""HCFS - Context-Aware Hierarchical Context File System"""
pass
@main.command()
@click.option("--mount-point", "-m", required=True, help="Mount point for HCFS")
@click.option("--db-path", "-d", default="hcfs_context.db", help="Database path")
@click.option("--foreground", "-f", is_flag=True, help="Run in foreground")
def mount(mount_point: str, db_path: str, foreground: bool):
"""Mount HCFS filesystem."""
async def run_filesystem():
"""Run the FUSE filesystem."""
context_db = ContextDatabase(db_path)
fs = HCFSFilesystem(context_db, mount_point)
fuse_options = set(pyfuse3.default_options)
fuse_options.add('fsname=hcfs')
if foreground:
fuse_options.add('debug')
pyfuse3.init(fs, mount_point, fuse_options)
try:
click.echo(f"HCFS mounted at {mount_point}")
click.echo(f"Database: {db_path}")
click.echo("Press Ctrl+C to unmount...")
await pyfuse3.main()
except KeyboardInterrupt:
click.echo("\\nUnmounting HCFS...")
finally:
pyfuse3.close(unmount=True)
try:
asyncio.run(run_filesystem())
except Exception as e:
click.echo(f"Error: {e}", err=True)
sys.exit(1)
@main.command()
@click.option("--db-path", "-d", default="hcfs_context.db", help="Database path")
@click.option("--host", default="127.0.0.1", help="API server host")
@click.option("--port", default=8000, help="API server port")
def serve(db_path: str, host: str, port: int):
"""Start HCFS API server."""
app = create_app(db_path)
click.echo(f"Starting HCFS API server on {host}:{port}")
click.echo(f"Database: {db_path}")
click.echo(f"API docs: http://{host}:{port}/docs")
uvicorn.run(app, host=host, port=port)
@main.command()
@click.option("--db-path", "-d", default="hcfs_context.db", help="Database path")
@click.argument("path")
@click.argument("content")
@click.option("--author", "-a", help="Context author")
@click.option("--summary", "-s", help="Context summary")
def push(db_path: str, path: str, content: str, author: Optional[str], summary: Optional[str]):
"""Push context to a path."""
context_db = ContextDatabase(db_path)
embedding_manager = EmbeddingManager(context_db)
context = Context(
id=None,
path=path,
content=content,
summary=summary,
author=author or "cli_user"
)
context_id = embedding_manager.store_context_with_embedding(context)
click.echo(f"Context stored with ID: {context_id}")
@main.command()
@click.option("--db-path", "-d", default="hcfs_context.db", help="Database path")
@click.argument("path")
@click.option("--depth", default=1, help="Inheritance depth")
def get(db_path: str, path: str, depth: int):
"""Get contexts for a path."""
context_db = ContextDatabase(db_path)
contexts = context_db.get_context_by_path(path, depth=depth)
if not contexts:
click.echo("No contexts found for path")
return
for ctx in contexts:
click.echo(f"\\n--- Context ID: {ctx.id} ---")
click.echo(f"Path: {ctx.path}")
click.echo(f"Author: {ctx.author}")
click.echo(f"Created: {ctx.created_at}")
click.echo(f"Content: {ctx.content}")
if ctx.summary:
click.echo(f"Summary: {ctx.summary}")
@main.command()
@click.option("--db-path", "-d", default="hcfs_context.db", help="Database path")
@click.argument("query")
@click.option("--path-prefix", "-p", help="Path prefix filter")
@click.option("--top-k", "-k", default=5, help="Number of results")
@click.option("--search-type", "-t", default="hybrid",
type=click.Choice(["semantic", "hybrid"]), help="Search type")
def search(db_path: str, query: str, path_prefix: Optional[str], top_k: int, search_type: str):
"""Search contexts."""
context_db = ContextDatabase(db_path)
embedding_manager = EmbeddingManager(context_db)
if search_type == "semantic":
results = embedding_manager.semantic_search(query, path_prefix, top_k)
else:
results = embedding_manager.hybrid_search(query, path_prefix, top_k)
if not results:
click.echo("No results found")
return
click.echo(f"Found {len(results)} results:\\n")
for ctx, score in results:
click.echo(f"Score: {score:.4f} | Path: {ctx.path} | ID: {ctx.id}")
click.echo(f"Content: {ctx.content[:100]}...")
click.echo()
@main.command()
@click.option("--db-path", "-d", default="hcfs_context.db", help="Database path")
def init(db_path: str):
"""Initialize HCFS database."""
context_db = ContextDatabase(db_path)
click.echo(f"HCFS database initialized at {db_path}")
if __name__ == "__main__":
main()