Files
chemenu/tools/chemenu/commands/log_append.py
T
torben 18ae28f918
CI / verify (push) Failing after 32s
Release / release (push) Successful in 38s
Chemenu 2.1.0 - deterministischer Wissenskompiler
Chemenu kompiliert Rohnotizen zu einem verlinkten, quellengebundenen Wiki:
raw/ -> types/ + tools/ -> kb/ -> reports/. Was mechanisch ist, macht
tools/wikitool; was Urteil braucht, macht ein Agent unter Contracts, deren
Grenzen in Code durchgesetzt sind statt im Prompt.

Dieser Commit ist der Startpunkt der oeffentlichen Historie. Die vorherige
Entwicklung fand in einer privaten Instanz statt und ist nicht Teil dieses
Repositorys; ihre Erzaehlung steht vollstaendig in CHANGES.md, das mit 44
Eintraegen von 0.1.0 bis 2.1.0 erhalten geblieben ist.

Der mitgelieferte Korpus ist ein Testbett und eine Demo: 170 Seiten ueber den
Stack selbst - Gates, Lint, Versionierung, Suche, das Wiki-Muster. Er
dokumentiert das Werkzeug mit den eigenen Mitteln des Werkzeugs.

Lizenz: AGPL-3.0 fuer den Stack (tools/, types/), CC-BY-4.0 fuer die Inhalte.
Die Grenze zwischen beiden ist der Dateiplan, den dist export berechnet -
siehe NOTICE.
2026-09-01 16:26:14 +02:00

85 lines
3.2 KiB
Python

"""Append correctly-formatted entries to wiki/log.md."""
from __future__ import annotations
import re
from pathlib import Path
from typing import Optional
import typer
from chemenu import config
from chemenu.commands._util import fail, rel_path, success, today_iso
app = typer.Typer(help="Manage wiki/log.md.")
VALID_OPS = ["ingest", "query", "lint", "create", "update", "delete", "rename"]
# Matches the "## [YYYY-MM-DD] op | title" heading `format_log_entry` writes,
# in file order (oldest first, since entries are appended).
LOG_ENTRY_RE = re.compile(r"^## \[(\d{4}-\d{2}-\d{2})\] (\S+) \| (.+)$", re.MULTILINE)
def format_log_entry(op: str, title: str, body: str = "", today: Optional[str] = None) -> str:
today = today or today_iso()
entry = f"## [{today}] {op} | {title}\n"
if body.strip():
entry += f"\n{body.strip()}\n"
entry += "\n---\n"
return entry
def parse_log_entries(text: str) -> list[tuple[str, str, str]]:
"""Return every logged (date, op, title) entry in file order."""
return [(m.group(1), m.group(2), m.group(3)) for m in LOG_ENTRY_RE.finditer(text)]
def ingests_since_last_lint(entries: list[tuple[str, str, str]]) -> int:
"""Count `ingest` entries logged after the most recent `lint` entry (or
since the start of the log, if it has never been linted).
This is the deterministic count behind the Maintenance Schedule's "every
10 sources" full-lint cadence: nothing else in the system tracks it, so
without this the claim was prose with no enforcement - an agent (or user)
had to remember to count."""
count = 0
for _date, op, _title in entries:
if op == "lint":
count = 0
elif op == "ingest":
count += 1
return count
@app.command("append")
def log_append(
op: str = typer.Option(..., "--op", help="|".join(VALID_OPS)),
title: str = typer.Option(..., "--title", help="Brief description, e.g. a source path"),
body: str = typer.Option("", "--body", help="Optional multi-line details"),
body_file: Optional[Path] = typer.Option(None, "--body-file", help="Read the body from a file instead of --body"),
):
if op not in VALID_OPS:
fail(f"--op must be one of {VALID_OPS}")
text = body
if body_file:
text = body_file.read_text(encoding="utf-8")
entry = format_log_entry(op, title, text)
with config.LOG_FILE.open("a", encoding="utf-8") as f:
f.write("\n" + entry)
success(f"Appended log entry to {rel_path(config.LOG_FILE)}")
@app.command("status")
def log_status():
"""Report how many `ingest` operations have been logged since the last
`lint` - the deterministic trigger for the Maintenance Schedule's "every
10 sources" full-lint cadence. Read-only."""
if not config.LOG_FILE.exists():
success("No wiki/log.md yet; nothing logged.")
return
entries = parse_log_entries(config.LOG_FILE.read_text(encoding="utf-8"))
count = ingests_since_last_lint(entries)
typer.echo(f"Ingests since last lint: {count}")
if count >= 10:
typer.echo("Threshold reached (>=10) - run the wiki-lint skill (`tools/wikitool lint ...`) next.")
success(f"{len(entries)} total log entries in {rel_path(config.LOG_FILE)}.")