164 lines
5.2 KiB
Python
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() |