diff --git a/assets/sourceos/bin/turtle-mesh-serve b/assets/sourceos/bin/turtle-mesh-serve index 10015716d4d..b1ed8515c02 100755 --- a/assets/sourceos/bin/turtle-mesh-serve +++ b/assets/sourceos/bin/turtle-mesh-serve @@ -30,11 +30,12 @@ import urllib.request from http.server import BaseHTTPRequestHandler, HTTPServer from pathlib import Path -MESH_DIR = Path(os.getenv("XDG_STATE_HOME", str(Path.home() / ".local/state"))) / "sourceos" / "memory-mesh" -STATUS_DIR = Path(os.getenv("XDG_STATE_HOME", str(Path.home() / ".local/state"))) / "sourceos" / "status" -NOTES_DIR = Path(os.getenv("GOOSE_NOTES_DIR", str(Path.home() / "notes"))) -BB_SUPPORT = Path.home() / "Library" / "Application Support" / "BearBrowser" -BACKLINKS_PATH = Path(os.getenv("XDG_STATE_HOME", str(Path.home() / ".local/state"))) / "sourceos" / "notes-backlinks.json" +MESH_DIR = Path(os.getenv("XDG_STATE_HOME", str(Path.home() / ".local/state"))) / "sourceos" / "memory-mesh" +STATUS_DIR = Path(os.getenv("XDG_STATE_HOME", str(Path.home() / ".local/state"))) / "sourceos" / "status" +NOTES_DIR = Path(os.getenv("GOOSE_NOTES_DIR", str(Path.home() / "notes"))) +BB_SUPPORT = Path.home() / "Library" / "Application Support" / "BearBrowser" +BACKLINKS_PATH = Path(os.getenv("XDG_STATE_HOME", str(Path.home() / ".local/state"))) / "sourceos" / "notes-backlinks.json" +RUNBOOKS_DIR = Path(os.getenv("XDG_STATE_HOME", str(Path.home() / ".local/state"))) / "sourceos" / "runbooks" DEFAULT_PORT = int(os.getenv("TURTLE_MESH_PORT", "7788")) @@ -102,17 +103,35 @@ def gather_state() -> dict: for p in sorted(NOTES_DIR.glob("*.md"), key=lambda f: f.stat().st_mtime, reverse=True)[:8]: notes.append({"name": p.name, "mtime": p.stat().st_mtime}) + import re as _re + runbooks: list[dict] = [] + if RUNBOOKS_DIR.exists(): + for p in sorted(RUNBOOKS_DIR.glob("*.yaml"), key=lambda f: f.stat().st_mtime, reverse=True)[:12]: + try: + text = p.read_text(errors="replace") + name_m = _re.search(r"^name:\s*(.+)$", text, _re.MULTILINE) + desc_m = _re.search(r"^description:\s*(.+)$", text, _re.MULTILINE) + tags_m = _re.search(r"^tags:\s*\[(.+)\]", text, _re.MULTILINE) + runbooks.append({ + "name": name_m.group(1).strip() if name_m else p.stem, + "description": desc_m.group(1).strip() if desc_m else "", + "tags": [t.strip() for t in tags_m.group(1).split(",")] if tags_m else [], + }) + except Exception: + pass + return { - "ts": datetime.datetime.now().isoformat(), - "active": active, - "mesh": mesh, - "ci": ci, - "pr": pr, - "noetica": noetica, - "board": board, - "bb_cands": bb_cands, - "notes": notes, + "ts": datetime.datetime.now().isoformat(), + "active": active, + "mesh": mesh, + "ci": ci, + "pr": pr, + "noetica": noetica, + "board": board, + "bb_cands": bb_cands, + "notes": notes, "searxng_ok": searxng_ok, + "runbooks": runbooks, } @@ -273,6 +292,10 @@ h2{font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:.08em

Recent Notes

+
+

Runbooks

+
+
@@ -349,6 +372,12 @@ function render(state) { document.getElementById('notes').innerHTML = (state.notes || []).map(n => `
📝 ${esc(n.name)}
` ).join('') || '— no notes' + + // Runbooks + const rbs = state.runbooks || [] + document.getElementById('runbooks-list').innerHTML = rbs.length + ? rbs.map(r => `${esc(r.name)}`).join('') + : 'none yet — try: rb new deploy' } // SSE connection with auto-reconnect diff --git a/assets/sourceos/shell/turtle-shell-init.zsh b/assets/sourceos/shell/turtle-shell-init.zsh index 71bbcbf0e12..5949c8825b7 100644 --- a/assets/sourceos/shell/turtle-shell-init.zsh +++ b/assets/sourceos/shell/turtle-shell-init.zsh @@ -676,6 +676,10 @@ note() { printf '\e[38;2;57;197;207m◆\e[0m Captured: \e[38;2;230;237;243m%s\e[0m\n' "$_fname" printf ' Open with: \e[2m$EDITOR %s\e[0m\n' "$_fname" + # Auto-extract facts to persistent memory (background, non-blocking) + local _mbin_note + _mbin_note="$(dirname "$(readlink -f "${(%):-%x}" 2>/dev/null || echo "${0:A}")")/../bin/turtle-noetica-memory" + [[ -x "$_mbin_note" ]] && python3 "$_mbin_note" extract "$_title" &! 2>/dev/null } # note_watch — manually start/restart the backlinks watch daemon @@ -1522,7 +1526,15 @@ In 1-2 lines: what likely went wrong and how to fix it. Be direct, no preamble." if (( _triage_age < 90 )); then local _triage; _triage="$(< "$_TURTLE_TRIAGE_FILE")" if [[ -n "$_triage" ]]; then - printf '\n\e[38;2;57;197;207m◆ Noetica:\e[0m \e[38;2;139;148;158m%s\e[0m\n' "$_triage" + printf '\n\e[38;2;57;197;197m◆ Noetica:\e[0m \e[38;2;139;148;158m%s\e[0m\n' "$_triage" + # Suggest a matching runbook alongside the triage + local _rb_bin + _rb_bin="$(dirname "$(readlink -f "${(%):-%x}" 2>/dev/null || echo "${0:A}")")/../bin/turtle-runbook" + if [[ -x "$_rb_bin" && -n "${_TURTLE_PERF_CMD:-}" ]]; then + local _rb_match + _rb_match="$(python3 "$_rb_bin" search "${_TURTLE_PERF_CMD:0:60}" 2>/dev/null | head -1)" + [[ -n "$_rb_match" ]] && printf ' \e[2m◈ runbook: %s\e[0m\n' "$_rb_match" + fi fi fi rm -f "$_TURTLE_TRIAGE_FILE"