📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-06-02 16:01:55 +00:00
parent 8696cd9e52
commit ac7fffe532
834 changed files with 153251 additions and 1426 deletions
@@ -0,0 +1,62 @@
"""Shared helpers for user-thoughts scripts."""
import re
from pathlib import Path
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
return None
def find_skill_dir() -> Path | None:
"""Find the installed user-thoughts skill directory."""
script_dir = Path(__file__).resolve().parent
skill_dir = script_dir.parent
if (skill_dir / "SKILL.md").exists():
return skill_dir
return None
def read_define_ini(ustht: Path) -> dict:
"""Read define.ini and return key/value pairs."""
ini = ustht / "define.ini"
if not ini.exists():
return {}
result = {}
for line in ini.read_text(encoding="utf-8").splitlines():
line = line.strip()
if "=" in line and not line.startswith("#"):
k, v = line.split("=", 1)
result[k.strip()] = v.strip()
return result
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")
def is_processed(filepath: Path) -> 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()
return first_line == "<!-- processed -->"
def validate_dim_name(dim: str) -> bool:
"""Validate a dimension path made of safe kebab-case segments."""
reserved = {"raw", "ignored", "export", "define", "readme-ai"}
if not dim or len(dim) > 64 or ".." in dim or "\\" in dim or " " in dim:
return False
for part in dim.split("/"):
if part in reserved:
return False
if not part or not re.match(r"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$", part):
return False
return True
@@ -0,0 +1,125 @@
"""Manage ignored user-thought entries."""
import sys
from datetime import datetime
from pathlib import Path
from common import find_ustht
HELP = """Usage: python ignore_ops.py show|remove_last|add_suffix "text" [--help]
Subcommands:
show List entries under #ignored/
remove_last Remove the latest raw entry and move it to #ignored/
add_suffix "text" Add a suffix-ignored entry to #ignored/
"""
def find_last_raw_entry(raw_dir: Path):
"""Return (file path, line index, entry text) for the latest raw entry."""
files = sorted(raw_dir.glob("*.md"), reverse=True)
for f in files:
lines = f.read_text(encoding="utf-8").splitlines()
if lines and lines[0].strip() == "<!-- processed -->":
continue
for idx in range(len(lines) - 1, -1, -1):
if lines[idx].strip().startswith("- ["):
return f, idx, lines[idx]
return None, None, None
def remove_line(filepath: Path, idx: int):
"""Remove one line from a file."""
lines = filepath.read_text(encoding="utf-8").splitlines()
del lines[idx]
filepath.write_text("\n".join(lines) + ("\n" if lines else ""), encoding="utf-8")
def append_to_ignored(ignored_dir: Path, text: str, reason: str):
"""Append one ignored entry to today's ignored file."""
ignored_dir.mkdir(exist_ok=True)
today = datetime.now().strftime("%Y-%m-%d")
now = datetime.now().strftime("%H:%M")
f = ignored_dir / f"{today}.md"
clean = text.strip()
if " | suggested-dim:" in clean:
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")
else:
f.write_text(f"{entry}\n", encoding="utf-8")
def show_ignored(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)
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("- [")]
if entries:
print(f"#{f.name} ({len(entries)} entries):")
for entry in entries:
print(entry)
def remove_last(ustht: Path):
raw_dir = ustht / "raw"
if not raw_dir.exists():
print("No previous thought to ignore.")
return
filepath, idx, entry = find_last_raw_entry(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")
display = entry
if "] " in display:
display = display.split("] ", 1)[1]
if " | suggested-dim:" in display:
display = display.rsplit(" | suggested-dim:", 1)[0]
print(f"Ignored previous thought: {display}")
def add_suffix(ustht: Path, text: str):
append_to_ignored(ustht / "ignored", text, "ignored by suffix")
print("Ignored current message.")
def main():
if "--help" in sys.argv or "-h" in sys.argv:
print(HELP)
sys.exit(0)
ustht = find_ustht()
if ustht is None:
print("Error: .ustht/ was not found. Run /ustht init first.")
sys.exit(1)
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} show|remove_last|add_suffix \"text\"")
sys.exit(1)
cmd = sys.argv[1]
if cmd == "show":
show_ignored(ustht / "ignored")
elif cmd == "remove_last":
remove_last(ustht)
elif cmd == "add_suffix":
if len(sys.argv) < 3:
print("Error: add_suffix requires text.")
sys.exit(1)
add_suffix(ustht, sys.argv[2])
else:
print(f"Unknown command: {cmd}. Available: show, remove_last, add_suffix")
sys.exit(1)
if __name__ == "__main__":
main()
@@ -0,0 +1,63 @@
"""Initialize the .ustht/ runtime directory from templates."""
import shutil
import sys
from pathlib import Path
from common import find_skill_dir
HELP = """Usage: python init.py [--help]
Create .ustht/ in the current working directory, copy the runtime templates,
and create raw/, ignored/, and export/ directories. Existing .ustht/ content is
not overwritten.
"""
def copy_template(src: Path, dst: Path):
"""Copy template files while skipping symlinks."""
for item in src.rglob("*"):
rel = item.relative_to(src)
target = dst / rel
if item.is_symlink():
continue
if item.is_dir():
target.mkdir(parents=True, exist_ok=True)
else:
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(item, target)
def main():
if "--help" in sys.argv or "-h" in sys.argv:
print(HELP)
sys.exit(0)
target = Path.cwd() / ".ustht"
if target.exists():
print("Already initialized; .ustht/ exists, skipping creation.")
sys.exit(0)
skill_dir = find_skill_dir()
if skill_dir is None:
print("Error: SKILL.md was not found. Ensure this script is inside user-thoughts/scripts/.")
sys.exit(1)
template = skill_dir / "assets" / "Runtime-Template"
if not template.exists():
print(f"Error: template directory does not exist: {template}")
sys.exit(1)
target.mkdir()
copy_template(template, target)
for name in ["raw", "ignored", "export"]:
(target / name).mkdir(exist_ok=True)
define = target / "define.ini"
if not define.exists():
define.write_text("SKILL_STATUS=on\nINSTANT_STATUS=off\nLAST_SORTIN=\n", encoding="utf-8")
print("Initialized .ustht/.")
if __name__ == "__main__":
main()
@@ -0,0 +1,93 @@
"""Show the mdbase index or dimension content."""
import sys
from pathlib import Path
from common import find_ustht, validate_dim_name
HELP = """Usage: python show_mdbase.py show [--all|--dimension] [--help]
Subcommands:
show Show README.ai.md index
show --all List all dimensions and entry counts
show <dimension> Show one dimension file
"""
def show_index(mdbase: Path):
index = mdbase / "README.ai.md"
if not index.exists():
print("mdbase/README.ai.md does not exist.")
return
print(index.read_text(encoding="utf-8"))
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"))
def show_dim(mdbase: Path, dim: str):
if not validate_dim_name(dim):
print(f"Invalid dimension name: {dim}. Use lowercase letters, digits, hyphens, and optional / subdirectories.")
return
if dim == "backlog":
path = mdbase / "backlog.md"
else:
path = mdbase / "details" / f"{dim}.md"
if not path.exists():
print(f"mdbase/details/{dim}.md does not exist yet.")
return
print(path.read_text(encoding="utf-8"))
def show_all(mdbase: Path):
details = mdbase / "details"
if not details.exists():
print("mdbase/details/ does not exist.")
return
dims = list_dims(mdbase)
if not dims:
print("mdbase has no dimension files.")
return
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("- ")]
print(f" {dim}.md: {len(lines)} entries")
def main():
if "--help" in sys.argv or "-h" in sys.argv:
print(HELP)
sys.exit(0)
ustht = find_ustht()
if ustht is None:
print("Error: .ustht/ was not found. Run /ustht init first.")
sys.exit(1)
mdbase = ustht / "mdbase"
if not mdbase.exists():
print("mdbase is not initialized. Run /ustht init first.")
return
args = sys.argv[1:]
if not args or args[0] != "show":
print(f"Usage: {sys.argv[0]} show [--all|--dimension]")
sys.exit(1)
rest = args[1:]
if not rest:
show_index(mdbase)
elif rest[0] == "--all":
show_all(mdbase)
elif rest[0].startswith("--"):
show_dim(mdbase, rest[0][2:])
else:
show_dim(mdbase, rest[0])
if __name__ == "__main__":
main()
@@ -0,0 +1,42 @@
"""Show unprocessed raw files."""
import sys
from pathlib import Path
from common import find_ustht, is_processed
HELP = """Usage: python show_raw.py [--help]
Show unprocessed #raw/ files, including filenames, entry counts, and content.
"""
def main():
if "--help" in sys.argv or "-h" in sys.argv:
print(HELP)
sys.exit(0)
ustht = find_ustht()
if ustht is None:
print("Error: .ustht/ was not found. Run /ustht init first.")
sys.exit(1)
raw_dir = ustht / "raw"
if not raw_dir.exists():
print("No unprocessed records.")
return
files = [f for f in sorted(raw_dir.glob("*.md"), reverse=True) if not is_processed(f)]
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()
entry_count = sum(1 for line in content.splitlines() if line.strip().startswith("- ["))
print(f"#{f.name} ({entry_count} unprocessed entries):")
print(content)
print()
if __name__ == "__main__":
main()
@@ -0,0 +1,211 @@
"""Soft-maintain raw user-thought entries into mdbase."""
import re
import sys
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
HELP = """Usage: python sortin.py [--dry] [--help]
Soft maintenance: parse unprocessed #raw/*.md files, append entries to matching
mdbase dimensions, mark raw files as processed, and update LAST_SORTIN.
Options:
--dry Preview changes without writing
--help Show this help text
"""
def parse_raw_file(filepath: Path):
"""Parse raw entries from one file."""
entries = []
date = filepath.stem.split("-", 3)
if len(date) >= 3:
date = "-".join(date[:3])
else:
date = datetime.now().strftime("%Y-%m-%d")
for line in filepath.read_text(encoding="utf-8").splitlines():
line = line.strip()
match = re.match(r"^- \[(\d{2}:\d{2})\] (.*)$", line)
if not match:
continue
time, content = match.groups()
dim = "general"
text = content
if " | suggested-dim:" in content:
text, dim = content.rsplit(" | suggested-dim:", 1)
dim = dim.strip()
if not validate_dim_name(dim):
dim = "general"
entries.append({"time": time, "text": text.strip(), "dimension": dim, "date": date})
return entries
def dim_path(mdbase: Path, dim: str) -> Path:
"""Return the target file path for a dimension."""
if dim == "backlog":
return mdbase / "backlog.md"
return mdbase / "details" / f"{dim}.md"
def count_entries(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("- "))
def append_entries(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)
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")
content = path.read_text(encoding="utf-8").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)
insert_idx = len(content_lines)
for i in range(heading_idx + 1, len(content_lines)):
if content_lines[i].startswith("## "):
insert_idx = i
break
before = content_lines[:insert_idx]
after = content_lines[insert_idx:]
if before and before[-1].strip():
before.append("")
before.extend(lines)
if after:
before.append("")
before.extend(after)
content = "\n".join(before).rstrip()
else:
content = f"{content}\n\n{heading}\n\n{block}".rstrip()
path.write_text(content + "\n", encoding="utf-8")
def mark_processed(filepath: Path):
"""Insert the processed marker at the top of a raw file."""
content = filepath.read_text(encoding="utf-8")
if content.split("\n", 1)[0].strip() != "<!-- processed -->":
filepath.write_text("<!-- processed -->\n" + content, encoding="utf-8")
def update_index(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"))
rows = ["| File | Dimension | Entries |", "|------|-----------|---------|"]
backlog = mdbase / "backlog.md"
if backlog.exists():
rows.append(f"| [backlog.md](backlog.md) | backlog | {count_entries(backlog)} |")
for dim in dims:
path = details / f"{dim}.md"
rows.append(f"| [details/{dim}.md](details/{dim}.md) | {dim} | {count_entries(path)} |")
content = "\n".join([
"# user-thoughts mdbase Index",
"",
"This directory stores user-provided project decisions, constraints, preferences, and plans.",
"",
f"Last updated: {now}",
"",
"## Maintenance Rules",
"",
"- Preserve user wording and constraints.",
"- Append entries by date under `## yyyy-mm-dd` headings.",
"- Prefer existing dimensions before creating new ones.",
"- Mark deprecated content instead of silently deleting history.",
"",
"## Document Index",
"",
*rows,
"",
])
(mdbase / "README.ai.md").write_text(content, encoding="utf-8")
def main():
if "--help" in sys.argv or "-h" in sys.argv:
print(HELP)
sys.exit(0)
dry = "--dry" in sys.argv
ustht = find_ustht()
if ustht is None:
print("Error: .ustht/ was not found. Run /ustht init first.")
sys.exit(1)
cfg = read_define_ini(ustht)
if cfg.get("SKILL_STATUS") == "off":
print("SKILL is off; write ignored. Run /ustht skill on to enable it.")
sys.exit(0)
raw_dir = ustht / "raw"
if not raw_dir.exists():
print("No unprocessed records.")
return
raw_files = [f for f in sorted(raw_dir.glob("*.md")) if not is_processed(f)]
if not raw_files:
print("No unprocessed records. All raw files are marked processed.")
return
all_entries = []
entries_by_file = {}
for f in raw_files:
entries = parse_raw_file(f)
entries_by_file[f] = entries
all_entries.extend(entries)
if not all_entries:
print("No valid entries found in raw files.")
return
grouped = defaultdict(list)
for entry in all_entries:
grouped[entry["dimension"]].append(entry)
print("Preview mode:" if dry else f"Soft maintenance complete. Processed {len(all_entries)} thoughts:")
mdbase = ustht / "mdbase"
for dim, entries in sorted(grouped.items()):
target = dim_path(mdbase, dim)
label = f"{dim}.md" if target.exists() else f"{dim}.md [new dimension]"
sample = entries[0]["text"][:60]
print(f" -> {label}: +{len(entries)} ({sample})")
if dry:
print(f" {len(all_entries)} total entries; no files were changed.")
return
for dim, entries in grouped.items():
append_entries(dim_path(mdbase, dim), entries)
for f in raw_files:
if entries_by_file.get(f):
mark_processed(f)
now = datetime.now().strftime("%Y-%m-%d %H:%M")
cfg["LAST_SORTIN"] = now
write_define_ini(ustht, cfg)
update_index(mdbase)
print(f" LAST_SORTIN updated to {now}")
if __name__ == "__main__":
main()
@@ -0,0 +1,56 @@
"""Show current user-thoughts runtime status."""
import sys
from pathlib import Path
from common import find_ustht, read_define_ini, is_processed
HELP = """Usage: python status.py [--help]
Show SKILL_STATUS, INSTANT_STATUS, LAST_SORTIN, raw file counts, and mdbase
dimension counts.
"""
def count_raw(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))
return len(files), unprocessed
def count_dims(mdbase: Path):
"""Count dimension files under mdbase/details/."""
details = mdbase / "details"
if not details.exists():
return 0
return len(list(details.rglob("*.md")))
def main():
if "--help" in sys.argv or "-h" in sys.argv:
print(HELP)
sys.exit(0)
ustht = find_ustht()
if ustht is None:
print("Error: .ustht/ was not found. Run /ustht init first.")
sys.exit(1)
cfg = read_define_ini(ustht)
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")
print(f"SKILL_STATUS={skill_status}")
print(f"INSTANT_STATUS={instant_status}")
print(f"LAST_SORTIN={last_sortin}")
print(f"raw={unprocessed_raw} unprocessed / {total_raw} total")
print(f"dims={dims}")
if __name__ == "__main__":
main()
@@ -0,0 +1,68 @@
"""Toggle SKILL_STATUS and INSTANT_STATUS."""
import sys
from common import find_ustht, read_define_ini, write_define_ini
HELP = """Usage: python toggle.py skill|instant [on|off] [--help]
Subcommands:
skill Show SKILL_STATUS
skill on|off Set SKILL_STATUS
instant Show INSTANT_STATUS
instant on|off Set INSTANT_STATUS
Note: instant on requires SKILL_STATUS=on.
"""
def main():
if "--help" in sys.argv or "-h" in sys.argv:
print(HELP)
sys.exit(0)
ustht = find_ustht()
if ustht is None:
print("Error: .ustht/ was not found. Run /ustht init first.")
sys.exit(1)
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} skill|instant [on|off]")
sys.exit(1)
cmd = sys.argv[1]
if cmd not in {"skill", "instant"}:
print(f"Unknown command: {cmd}. Available: skill, instant")
sys.exit(1)
cfg = read_define_ini(ustht)
ini_key = "SKILL_STATUS" if cmd == "skill" else "INSTANT_STATUS"
if len(sys.argv) == 2:
print(f"{ini_key}={cfg.get(ini_key, 'unknown')}")
return
val = sys.argv[2]
if val not in {"on", "off"}:
print(f"Invalid value: {val}. Available values: on | off")
sys.exit(1)
if cmd == "instant" and val == "on" and cfg.get("SKILL_STATUS") == "off":
print("SKILL is off; instant capture cannot be enabled. Run /ustht skill on first.")
sys.exit(1)
cfg[ini_key] = val
if cmd == "skill" and val == "off":
cfg["INSTANT_STATUS"] = "off"
write_define_ini(ustht, cfg)
if cmd == "skill":
if val == "off":
print("SKILL is off. Instant capture has been paused.")
else:
print("SKILL is on.")
else:
print("Instant capture is on." if val == "on" else "Instant capture is off.")
if __name__ == "__main__":
main()
@@ -0,0 +1,106 @@
"""Append one thought to today's raw file."""
import sys
from datetime import datetime
from pathlib import Path
from common import find_ustht, read_define_ini, validate_dim_name
HELP = """Usage: python write_raw.py "thought text" [--dim dimension] [--help]
Append one thought to today's #raw/ markdown file.
Arguments:
"thought text" Thought text to record (required)
--dim dimension Suggested dimension, such as rules or ui/outline
--help Show this help text
Behavior:
- If today's raw file is already processed, creates a numbered file such as 2026-06-01-2.md.
- If the day has more than five raw entries, suggests /ustht sortin.
- If SKILL_STATUS=off, exits without writing.
"""
def count_today_raw(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")
first_line = content.split("\n", 1)[0].strip()
if first_line == "<!-- processed -->":
continue
count += sum(1 for line in content.splitlines() if line.strip().startswith("- ["))
return count
def main():
if "--help" in sys.argv or "-h" in sys.argv:
print(HELP)
sys.exit(0)
ustht = find_ustht()
if ustht is None:
print("Error: .ustht/ was not found. Run /ustht init first.")
sys.exit(1)
cfg = read_define_ini(ustht)
if cfg.get("SKILL_STATUS") == "off":
print("SKILL is off; write ignored.")
sys.exit(0)
thought = None
dim = None
args = sys.argv[1:]
i = 0
while i < len(args):
if args[i] == "--dim" and i + 1 < len(args):
dim = args[i + 1]
i += 2
elif thought is None:
thought = args[i]
i += 1
else:
i += 1
if not thought:
print("Error: missing thought text.")
print(f"Usage: {sys.argv[0]} \"thought text\" [--dim dimension]")
sys.exit(1)
if dim and not validate_dim_name(dim):
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)
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()
if first_line == "<!-- processed -->":
seq = 2
while (raw_dir / f"{today}-{seq}.md").exists():
seq += 1
raw_file = raw_dir / f"{today}-{seq}.md"
thought_clean = thought.replace("\n", " ").replace("\r", "")
suffix = f" | suggested-dim:{dim}" if dim else ""
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")
else:
raw_file.write_text(f"{entry}\n", encoding="utf-8")
count = count_today_raw(raw_dir)
if count > 5:
print(f"Today has {count} recorded thoughts. Consider running /ustht sortin.")
if __name__ == "__main__":
main()