📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-06-07 16:02:11 +00:00
parent 772a1da63c
commit ac39ab52f3
219 changed files with 5905 additions and 833 deletions
@@ -3,13 +3,20 @@ import re
from pathlib import Path
class UsthtSafetyError(RuntimeError):
"""Raised when a runtime path would escape .ustht/."""
def find_ustht() -> Path | None:
"""Find .ustht/ in the current directory or one of its parents."""
cwd = Path.cwd()
for d in [cwd, *cwd.parents]:
ustht = d / ".ustht"
if ustht.is_dir():
return ustht
if ustht.exists():
if ustht.is_symlink():
raise UsthtSafetyError(f"Refusing symlinked runtime directory: {ustht}")
if ustht.is_dir():
return ustht.resolve()
return None
@@ -28,7 +35,7 @@ def read_define_ini(ustht: Path) -> dict:
if not ini.exists():
return {}
result = {}
for line in ini.read_text(encoding="utf-8").splitlines():
for line in safe_read_text(ustht, ini).splitlines():
line = line.strip()
if "=" in line and not line.startswith("#"):
k, v = line.split("=", 1)
@@ -40,15 +47,95 @@ def write_define_ini(ustht: Path, cfg: dict):
"""Replace define.ini with the provided key/value pairs."""
ini = ustht / "define.ini"
lines = [f"{k}={v}" for k, v in cfg.items()]
ini.write_text("\n".join(lines) + "\n", encoding="utf-8")
safe_write_text(ustht, ini, "\n".join(lines) + "\n")
def is_processed(filepath: Path) -> bool:
def is_processed(filepath: Path, ustht: Path | None = None) -> bool:
"""Return true when the first raw-file line is the processed marker."""
first_line = filepath.read_text(encoding="utf-8").split("\n", 1)[0].strip()
content = safe_read_text(ustht, filepath) if ustht else filepath.read_text(encoding="utf-8")
first_line = content.split("\n", 1)[0].strip()
return first_line == "<!-- processed -->"
def ensure_runtime_path(ustht: Path, path: Path, *, must_exist: bool = False) -> Path:
"""Return a path only if its real location stays inside .ustht/."""
base = Path(ustht)
if base.is_symlink():
raise UsthtSafetyError(f"Refusing symlinked runtime directory: {base}")
base_real = base.resolve(strict=True)
target = Path(path)
if not target.is_absolute():
target = base_real / target
if target.exists() and target.is_symlink():
raise UsthtSafetyError(f"Refusing symlinked runtime path: {target}")
if must_exist and not target.exists():
raise UsthtSafetyError(f"Runtime path does not exist: {target}")
target_real = target.resolve(strict=must_exist)
try:
target_real.relative_to(base_real)
except ValueError as exc:
raise UsthtSafetyError(f"Runtime path escapes .ustht/: {target}") from exc
rel = target.relative_to(base_real)
current = base_real
for part in rel.parts:
current = current / part
if current.exists() and current.is_symlink():
raise UsthtSafetyError(f"Refusing symlinked runtime path: {current}")
return target
def ensure_runtime_dir(ustht: Path, path: Path, *, create: bool = False) -> Path:
"""Return a safe runtime directory, creating it when requested."""
directory = ensure_runtime_path(ustht, path, must_exist=False)
if create:
directory.mkdir(parents=True, exist_ok=True)
if directory.exists() and not directory.is_dir():
raise UsthtSafetyError(f"Runtime path is not a directory: {directory}")
return directory
def safe_read_text(ustht: Path | None, path: Path) -> str:
"""Read a runtime file after symlink and containment checks."""
safe_path = ensure_runtime_path(ustht, path, must_exist=True) if ustht else path
return safe_path.read_text(encoding="utf-8")
def safe_write_text(ustht: Path, path: Path, content: str):
"""Write a runtime file after symlink and containment checks."""
safe_path = ensure_runtime_path(ustht, path, must_exist=False)
ensure_runtime_dir(ustht, safe_path.parent, create=True)
safe_path.write_text(content, encoding="utf-8")
def safe_markdown_files(ustht: Path, directory: Path, *, reverse: bool = False) -> list[Path]:
"""List safe markdown files under one runtime directory."""
safe_dir = ensure_runtime_dir(ustht, directory)
if not safe_dir.exists():
return []
files = []
for file_path in safe_dir.glob("*.md"):
safe_path = ensure_runtime_path(ustht, file_path, must_exist=True)
if safe_path.is_file():
files.append(safe_path)
return sorted(files, reverse=reverse)
def safe_markdown_tree(ustht: Path, directory: Path) -> list[Path]:
"""List safe markdown files recursively under one runtime directory."""
safe_dir = ensure_runtime_dir(ustht, directory)
if not safe_dir.exists():
return []
files = []
for file_path in safe_dir.rglob("*.md"):
safe_path = ensure_runtime_path(ustht, file_path, must_exist=True)
if safe_path.is_file():
files.append(safe_path)
return sorted(files)
def validate_dim_name(dim: str) -> bool:
"""Validate a dimension path made of safe kebab-case segments."""
reserved = {"raw", "ignored", "export", "define", "readme-ai"}
@@ -3,7 +3,7 @@ import sys
from datetime import datetime
from pathlib import Path
from common import find_ustht
from common import ensure_runtime_dir, find_ustht, safe_markdown_files, safe_read_text, safe_write_text
HELP = """Usage: python ignore_ops.py show|remove_last|add_suffix "text" [--help]
@@ -14,11 +14,11 @@ Subcommands:
"""
def find_last_raw_entry(raw_dir: Path):
def find_last_raw_entry(ustht: Path, raw_dir: Path):
"""Return (file path, line index, entry text) for the latest raw entry."""
files = sorted(raw_dir.glob("*.md"), reverse=True)
files = safe_markdown_files(ustht, raw_dir, reverse=True)
for f in files:
lines = f.read_text(encoding="utf-8").splitlines()
lines = safe_read_text(ustht, f).splitlines()
if lines and lines[0].strip() == "<!-- processed -->":
continue
for idx in range(len(lines) - 1, -1, -1):
@@ -27,16 +27,16 @@ def find_last_raw_entry(raw_dir: Path):
return None, None, None
def remove_line(filepath: Path, idx: int):
def remove_line(ustht: Path, filepath: Path, idx: int):
"""Remove one line from a file."""
lines = filepath.read_text(encoding="utf-8").splitlines()
lines = safe_read_text(ustht, filepath).splitlines()
del lines[idx]
filepath.write_text("\n".join(lines) + ("\n" if lines else ""), encoding="utf-8")
safe_write_text(ustht, filepath, "\n".join(lines) + ("\n" if lines else ""))
def append_to_ignored(ignored_dir: Path, text: str, reason: str):
def append_to_ignored(ustht: Path, ignored_dir: Path, text: str, reason: str):
"""Append one ignored entry to today's ignored file."""
ignored_dir.mkdir(exist_ok=True)
ignored_dir = ensure_runtime_dir(ustht, ignored_dir, create=True)
today = datetime.now().strftime("%Y-%m-%d")
now = datetime.now().strftime("%H:%M")
f = ignored_dir / f"{today}.md"
@@ -45,23 +45,23 @@ def append_to_ignored(ignored_dir: Path, text: str, reason: str):
clean = clean.rsplit(" | suggested-dim:", 1)[0]
entry = f"- [{now}] {clean} ({reason})"
if f.exists():
content = f.read_text(encoding="utf-8").rstrip()
f.write_text(f"{content}\n{entry}\n", encoding="utf-8")
content = safe_read_text(ustht, f).rstrip()
safe_write_text(ustht, f, f"{content}\n{entry}\n")
else:
f.write_text(f"{entry}\n", encoding="utf-8")
safe_write_text(ustht, f, f"{entry}\n")
def show_ignored(ignored_dir: Path):
def show_ignored(ustht: Path, ignored_dir: Path):
"""Print all ignored entries."""
if not ignored_dir.exists():
print("No ignored entries.")
return
files = sorted(ignored_dir.glob("*.md"), reverse=True)
files = safe_markdown_files(ustht, ignored_dir, reverse=True)
if not files:
print("No ignored entries.")
return
for f in files:
entries = [line for line in f.read_text(encoding="utf-8").splitlines() if line.strip().startswith("- [")]
entries = [line for line in safe_read_text(ustht, f).splitlines() if line.strip().startswith("- [")]
if entries:
print(f"#{f.name} ({len(entries)} entries):")
for entry in entries:
@@ -73,12 +73,12 @@ def remove_last(ustht: Path):
if not raw_dir.exists():
print("No previous thought to ignore.")
return
filepath, idx, entry = find_last_raw_entry(raw_dir)
filepath, idx, entry = find_last_raw_entry(ustht, raw_dir)
if filepath is None:
print("No previous thought to ignore.")
return
remove_line(filepath, idx)
append_to_ignored(ustht / "ignored", entry, "ignored with --last")
remove_line(ustht, filepath, idx)
append_to_ignored(ustht, ustht / "ignored", entry, "ignored with --last")
display = entry
if "] " in display:
display = display.split("] ", 1)[1]
@@ -88,7 +88,7 @@ def remove_last(ustht: Path):
def add_suffix(ustht: Path, text: str):
append_to_ignored(ustht / "ignored", text, "ignored by suffix")
append_to_ignored(ustht, ustht / "ignored", text, "ignored by suffix")
print("Ignored current message.")
@@ -108,7 +108,7 @@ def main():
cmd = sys.argv[1]
if cmd == "show":
show_ignored(ustht / "ignored")
show_ignored(ustht, ustht / "ignored")
elif cmd == "remove_last":
remove_last(ustht)
elif cmd == "add_suffix":
@@ -3,7 +3,7 @@ import shutil
import sys
from pathlib import Path
from common import find_skill_dir
from common import UsthtSafetyError, find_skill_dir
HELP = """Usage: python init.py [--help]
@@ -34,6 +34,8 @@ def main():
target = Path.cwd() / ".ustht"
if target.exists():
if target.is_symlink():
raise UsthtSafetyError(f"Refusing symlinked runtime directory: {target}")
print("Already initialized; .ustht/ exists, skipping creation.")
sys.exit(0)
@@ -2,7 +2,7 @@
import sys
from pathlib import Path
from common import find_ustht, validate_dim_name
from common import find_ustht, safe_markdown_tree, safe_read_text, validate_dim_name
HELP = """Usage: python show_mdbase.py show [--all|--dimension] [--help]
@@ -18,14 +18,14 @@ def show_index(mdbase: Path):
if not index.exists():
print("mdbase/README.ai.md does not exist.")
return
print(index.read_text(encoding="utf-8"))
print(safe_read_text(mdbase.parent, index))
def list_dims(mdbase: Path):
details = mdbase / "details"
if not details.exists():
return []
return sorted(p.relative_to(details).with_suffix("").as_posix() for p in details.rglob("*.md"))
return sorted(p.relative_to(details).with_suffix("").as_posix() for p in safe_markdown_tree(mdbase.parent, details))
def show_dim(mdbase: Path, dim: str):
@@ -39,7 +39,7 @@ def show_dim(mdbase: Path, dim: str):
if not path.exists():
print(f"mdbase/details/{dim}.md does not exist yet.")
return
print(path.read_text(encoding="utf-8"))
print(safe_read_text(mdbase.parent, path))
def show_all(mdbase: Path):
@@ -54,7 +54,7 @@ def show_all(mdbase: Path):
print(f"mdbase has {len(dims)} dimensions:")
for dim in dims:
path = details / f"{dim}.md"
lines = [line for line in path.read_text(encoding="utf-8").splitlines() if line.strip().startswith("- ")]
lines = [line for line in safe_read_text(mdbase.parent, path).splitlines() if line.strip().startswith("- ")]
print(f" {dim}.md: {len(lines)} entries")
@@ -2,7 +2,7 @@
import sys
from pathlib import Path
from common import find_ustht, is_processed
from common import find_ustht, is_processed, safe_markdown_files, safe_read_text
HELP = """Usage: python show_raw.py [--help]
@@ -25,13 +25,13 @@ def main():
print("No unprocessed records.")
return
files = [f for f in sorted(raw_dir.glob("*.md"), reverse=True) if not is_processed(f)]
files = [f for f in safe_markdown_files(ustht, raw_dir, reverse=True) if not is_processed(f, ustht)]
if not files:
print("No unprocessed records. All raw files are marked processed.")
return
for f in files:
content = f.read_text(encoding="utf-8").strip()
content = safe_read_text(ustht, f).strip()
entry_count = sum(1 for line in content.splitlines() if line.strip().startswith("- ["))
print(f"#{f.name} ({entry_count} unprocessed entries):")
print(content)
@@ -5,7 +5,18 @@ from collections import defaultdict
from datetime import datetime
from pathlib import Path
from common import find_ustht, read_define_ini, write_define_ini, is_processed, validate_dim_name
from common import (
ensure_runtime_dir,
safe_markdown_files,
safe_markdown_tree,
safe_read_text,
safe_write_text,
find_ustht,
read_define_ini,
write_define_ini,
is_processed,
validate_dim_name,
)
HELP = """Usage: python sortin.py [--dry] [--help]
@@ -18,7 +29,7 @@ Options:
"""
def parse_raw_file(filepath: Path):
def parse_raw_file(ustht: Path, filepath: Path):
"""Parse raw entries from one file."""
entries = []
date = filepath.stem.split("-", 3)
@@ -27,7 +38,7 @@ def parse_raw_file(filepath: Path):
else:
date = datetime.now().strftime("%Y-%m-%d")
for line in filepath.read_text(encoding="utf-8").splitlines():
for line in safe_read_text(ustht, filepath).splitlines():
line = line.strip()
match = re.match(r"^- \[(\d{2}:\d{2})\] (.*)$", line)
if not match:
@@ -51,31 +62,31 @@ def dim_path(mdbase: Path, dim: str) -> Path:
return mdbase / "details" / f"{dim}.md"
def count_entries(path: Path) -> int:
def count_entries(ustht: Path, path: Path) -> int:
if not path.exists():
return 0
return sum(1 for line in path.read_text(encoding="utf-8").splitlines() if line.strip().startswith("- "))
return sum(1 for line in safe_read_text(ustht, path).splitlines() if line.strip().startswith("- "))
def append_entries(path: Path, entries):
def append_entries(ustht: Path, path: Path, entries):
"""Append entries grouped by date to one dimension file."""
by_date = defaultdict(list)
for entry in entries:
by_date[entry["date"]].append(entry)
path.parent.mkdir(parents=True, exist_ok=True)
ensure_runtime_dir(ustht, path.parent, create=True)
if not path.exists():
title = path.stem.replace("-", " ").title()
path.write_text(f"# {title}\n\n> Project memory for `{path.stem}`.\n\n", encoding="utf-8")
safe_write_text(ustht, path, f"# {title}\n\n> Project memory for `{path.stem}`.\n\n")
content = path.read_text(encoding="utf-8").rstrip()
content = safe_read_text(ustht, path).rstrip()
for date, date_entries in sorted(by_date.items()):
lines = [f"- {entry['text']}" for entry in date_entries]
block = "\n".join(lines)
heading = f"## {date}"
if heading in content:
content_lines = content.splitlines()
heading_idx = next(i for i, line in enumerate(content_lines) if line.strip() == heading)
content_lines = content.splitlines()
heading_idx = next((i for i, line in enumerate(content_lines) if line.strip() == heading), None)
if heading_idx is not None:
insert_idx = len(content_lines)
for i in range(heading_idx + 1, len(content_lines)):
if content_lines[i].startswith("## "):
@@ -92,31 +103,31 @@ def append_entries(path: Path, entries):
content = "\n".join(before).rstrip()
else:
content = f"{content}\n\n{heading}\n\n{block}".rstrip()
path.write_text(content + "\n", encoding="utf-8")
safe_write_text(ustht, path, content + "\n")
def mark_processed(filepath: Path):
def mark_processed(ustht: Path, filepath: Path):
"""Insert the processed marker at the top of a raw file."""
content = filepath.read_text(encoding="utf-8")
content = safe_read_text(ustht, filepath)
if content.split("\n", 1)[0].strip() != "<!-- processed -->":
filepath.write_text("<!-- processed -->\n" + content, encoding="utf-8")
safe_write_text(ustht, filepath, "<!-- processed -->\n" + content)
def update_index(mdbase: Path):
def update_index(ustht: Path, mdbase: Path):
"""Rebuild mdbase/README.ai.md with dimension counts."""
now = datetime.now().strftime("%Y-%m-%d %H:%M")
details = mdbase / "details"
dims = []
if details.exists():
dims = sorted(p.relative_to(details).with_suffix("").as_posix() for p in details.rglob("*.md"))
dims = sorted(p.relative_to(details).with_suffix("").as_posix() for p in safe_markdown_tree(ustht, details))
rows = ["| File | Dimension | Entries |", "|------|-----------|---------|"]
backlog = mdbase / "backlog.md"
if backlog.exists():
rows.append(f"| [backlog.md](backlog.md) | backlog | {count_entries(backlog)} |")
rows.append(f"| [backlog.md](backlog.md) | backlog | {count_entries(ustht, backlog)} |")
for dim in dims:
path = details / f"{dim}.md"
rows.append(f"| [details/{dim}.md](details/{dim}.md) | {dim} | {count_entries(path)} |")
rows.append(f"| [details/{dim}.md](details/{dim}.md) | {dim} | {count_entries(ustht, path)} |")
content = "\n".join([
"# user-thoughts mdbase Index",
@@ -137,7 +148,7 @@ def update_index(mdbase: Path):
*rows,
"",
])
(mdbase / "README.ai.md").write_text(content, encoding="utf-8")
safe_write_text(ustht, mdbase / "README.ai.md", content)
def main():
@@ -161,7 +172,7 @@ def main():
print("No unprocessed records.")
return
raw_files = [f for f in sorted(raw_dir.glob("*.md")) if not is_processed(f)]
raw_files = [f for f in safe_markdown_files(ustht, raw_dir) if not is_processed(f, ustht)]
if not raw_files:
print("No unprocessed records. All raw files are marked processed.")
return
@@ -169,7 +180,7 @@ def main():
all_entries = []
entries_by_file = {}
for f in raw_files:
entries = parse_raw_file(f)
entries = parse_raw_file(ustht, f)
entries_by_file[f] = entries
all_entries.extend(entries)
@@ -194,16 +205,16 @@ def main():
return
for dim, entries in grouped.items():
append_entries(dim_path(mdbase, dim), entries)
append_entries(ustht, dim_path(mdbase, dim), entries)
for f in raw_files:
if entries_by_file.get(f):
mark_processed(f)
mark_processed(ustht, f)
now = datetime.now().strftime("%Y-%m-%d %H:%M")
cfg["LAST_SORTIN"] = now
write_define_ini(ustht, cfg)
update_index(mdbase)
update_index(ustht, mdbase)
print(f" LAST_SORTIN updated to {now}")
@@ -2,7 +2,7 @@
import sys
from pathlib import Path
from common import find_ustht, read_define_ini, is_processed
from common import find_ustht, read_define_ini, is_processed, safe_markdown_files, safe_markdown_tree
HELP = """Usage: python status.py [--help]
@@ -11,21 +11,21 @@ dimension counts.
"""
def count_raw(raw_dir: Path):
def count_raw(ustht: Path, raw_dir: Path):
"""Return total and unprocessed raw file counts."""
if not raw_dir.exists():
return 0, 0
files = list(raw_dir.glob("*.md"))
unprocessed = sum(1 for f in files if not is_processed(f))
files = safe_markdown_files(ustht, raw_dir)
unprocessed = sum(1 for f in files if not is_processed(f, ustht))
return len(files), unprocessed
def count_dims(mdbase: Path):
def count_dims(ustht: Path, mdbase: Path):
"""Count dimension files under mdbase/details/."""
details = mdbase / "details"
if not details.exists():
return 0
return len(list(details.rglob("*.md")))
return len(safe_markdown_tree(ustht, details))
def main():
@@ -42,8 +42,8 @@ def main():
skill_status = cfg.get("SKILL_STATUS", "unknown")
instant_status = cfg.get("INSTANT_STATUS", "unknown")
last_sortin = cfg.get("LAST_SORTIN", "never") or "never"
total_raw, unprocessed_raw = count_raw(ustht / "raw")
dims = count_dims(ustht / "mdbase")
total_raw, unprocessed_raw = count_raw(ustht, ustht / "raw")
dims = count_dims(ustht, ustht / "mdbase")
print(f"SKILL_STATUS={skill_status}")
print(f"INSTANT_STATUS={instant_status}")
@@ -3,7 +3,15 @@ import sys
from datetime import datetime
from pathlib import Path
from common import find_ustht, read_define_ini, validate_dim_name
from common import (
ensure_runtime_dir,
safe_markdown_files,
safe_read_text,
safe_write_text,
find_ustht,
read_define_ini,
validate_dim_name,
)
HELP = """Usage: python write_raw.py "thought text" [--dim dimension] [--help]
@@ -21,12 +29,14 @@ Behavior:
"""
def count_today_raw(raw_dir: Path) -> int:
def count_today_raw(ustht: Path, raw_dir: Path) -> int:
"""Count unprocessed entries across today's raw files."""
today = datetime.now().strftime("%Y-%m-%d")
count = 0
for f in sorted(raw_dir.glob(f"{today}*.md")):
content = f.read_text(encoding="utf-8")
for f in safe_markdown_files(ustht, raw_dir):
if not f.name.startswith(today):
continue
content = safe_read_text(ustht, f)
first_line = content.split("\n", 1)[0].strip()
if first_line == "<!-- processed -->":
continue
@@ -72,15 +82,14 @@ def main():
print(f"Invalid dimension name: {dim}. Use lowercase letters, digits, hyphens, and optional / subdirectories.")
sys.exit(1)
raw_dir = ustht / "raw"
raw_dir.mkdir(exist_ok=True)
raw_dir = ensure_runtime_dir(ustht, ustht / "raw", create=True)
today = datetime.now().strftime("%Y-%m-%d")
now = datetime.now().strftime("%H:%M")
raw_file = raw_dir / f"{today}.md"
if raw_file.exists():
first_line = raw_file.read_text(encoding="utf-8").split("\n", 1)[0].strip()
first_line = safe_read_text(ustht, raw_file).split("\n", 1)[0].strip()
if first_line == "<!-- processed -->":
seq = 2
while (raw_dir / f"{today}-{seq}.md").exists():
@@ -92,12 +101,12 @@ def main():
entry = f"- [{now}] {thought_clean}{suffix}"
if raw_file.exists():
content = raw_file.read_text(encoding="utf-8").rstrip()
raw_file.write_text(f"{content}\n{entry}\n", encoding="utf-8")
content = safe_read_text(ustht, raw_file).rstrip()
safe_write_text(ustht, raw_file, f"{content}\n{entry}\n")
else:
raw_file.write_text(f"{entry}\n", encoding="utf-8")
safe_write_text(ustht, raw_file, f"{entry}\n")
count = count_today_raw(raw_dir)
count = count_today_raw(ustht, raw_dir)
if count > 5:
print(f"Today has {count} recorded thoughts. Consider running /ustht sortin.")