119 lines
3.9 KiB
Python
119 lines
3.9 KiB
Python
import os
|
|
import sys
|
|
import click
|
|
import yaml
|
|
|
|
from app import create_app
|
|
from app.config import load_config
|
|
from app.services.vault import Vault
|
|
from app.utils.time import now_iso_utc
|
|
|
|
|
|
@click.group()
|
|
def main():
|
|
"""PKM CLI."""
|
|
pass
|
|
|
|
|
|
@main.command("run")
|
|
@click.option("--vault", "vault_path", type=click.Path(file_okay=False, dir_okay=True, path_type=str), help="Path to the vault directory")
|
|
@click.option("--host", default="127.0.0.1", show_default=True, help="Host to bind")
|
|
@click.option("--port", default=5000, show_default=True, help="Port to bind")
|
|
@click.option("--debug/--no-debug", default=True, show_default=True, help="Enable/disable debug mode")
|
|
def runserver(vault_path: str | None, host: str, port: int, debug: bool):
|
|
"""Run the development server."""
|
|
cfg = load_config(vault_override=vault_path)
|
|
if cfg.VAULT_PATH:
|
|
os.environ["KB_VAULT_PATH"] = cfg.VAULT_PATH
|
|
os.environ["FLASK_DEBUG"] = "1" if debug else "0"
|
|
|
|
app = create_app(cfg)
|
|
app.run(host=host, port=port, debug=debug)
|
|
|
|
|
|
@main.command("init")
|
|
@click.option(
|
|
"--vault",
|
|
"vault_path",
|
|
required=True,
|
|
type=click.Path(file_okay=False, dir_okay=True, path_type=str),
|
|
help="Path to the vault directory to initialize",
|
|
)
|
|
@click.option(
|
|
"--sample/--no-sample",
|
|
default=True,
|
|
show_default=True,
|
|
help="Create a sample welcome note",
|
|
)
|
|
@click.option(
|
|
"--force",
|
|
is_flag=True,
|
|
default=False,
|
|
help="Proceed even if the directory is not empty",
|
|
)
|
|
def init_vault(vault_path: str, sample: bool, force: bool):
|
|
"""
|
|
Initialize a vault directory with notes/, attachments/, and .kb/.
|
|
Optionally create a sample note and a .kb/config.yml file.
|
|
"""
|
|
vault_path = os.path.abspath(vault_path)
|
|
exists = os.path.exists(vault_path)
|
|
|
|
if exists:
|
|
# Check emptiness only if directory exists
|
|
try:
|
|
entries = [e for e in os.listdir(vault_path) if not e.startswith(".")]
|
|
except FileNotFoundError:
|
|
entries = []
|
|
if entries and not force:
|
|
click.echo(
|
|
click.style(
|
|
f"Directory '{vault_path}' is not empty. Use --force to proceed.",
|
|
fg="yellow",
|
|
)
|
|
)
|
|
sys.exit(1)
|
|
else:
|
|
os.makedirs(vault_path, exist_ok=True)
|
|
|
|
v = Vault(vault_path)
|
|
v.ensure_structure()
|
|
|
|
# Write .kb/config.yml if it doesn't exist
|
|
kb_cfg_path = os.path.join(v.paths.kb, "config.yml")
|
|
if not os.path.exists(kb_cfg_path):
|
|
cfg_data = {
|
|
"version": 1,
|
|
"created": now_iso_utc(),
|
|
}
|
|
with open(kb_cfg_path, "w", encoding="utf-8") as f:
|
|
yaml.safe_dump(cfg_data, f, sort_keys=True)
|
|
click.echo(click.style(f"Created {kb_cfg_path}", fg="green"))
|
|
else:
|
|
click.echo(click.style(f"Exists {kb_cfg_path}", fg="blue"))
|
|
|
|
# Optionally create a sample note using the app context (reuses note writer)
|
|
if sample:
|
|
from app.services.notes_fs import create_note # lazy import to avoid app deps if not needed
|
|
|
|
# Build a minimal app context so create_note can resolve the vault
|
|
cfg = load_config(vault_override=vault_path)
|
|
app = create_app(cfg)
|
|
with app.app_context():
|
|
# Try to avoid duplicates by checking for an existing welcome file name
|
|
title = "Welcome to your vault"
|
|
body = (
|
|
"# Welcome to your vault\n\n"
|
|
"This is your first note. You can edit or delete it.\n\n"
|
|
"- Notes live under `notes/`\n"
|
|
"- Attachments go to `attachments/`\n"
|
|
"- App internals go to `.kb/`\n"
|
|
)
|
|
note = create_note(title=title, body=body, tags=["welcome"])
|
|
click.echo(click.style(f"Created sample note at {os.path.join(v.paths.root, note.rel_path)}", fg="green"))
|
|
|
|
click.echo(click.style(f"Vault initialized at {vault_path}", fg="green"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |