Milestone 1.

This commit is contained in:
2025-08-18 20:14:56 +02:00
parent 9d1623c739
commit 1646d7b827
23 changed files with 1684 additions and 1275 deletions

91
cli.py
View File

@@ -1,7 +1,12 @@
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()
@@ -17,9 +22,7 @@ def main():
@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."""
# Prefer CLI vault over env
cfg = load_config(vault_override=vault_path)
# Mirror into environment for consistency if needed
if cfg.VAULT_PATH:
os.environ["KB_VAULT_PATH"] = cfg.VAULT_PATH
os.environ["FLASK_DEBUG"] = "1" if debug else "0"
@@ -28,5 +31,89 @@ def runserver(vault_path: str | None, host: str, port: int, debug: bool):
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()