From c8ad5f3e2843e5b5fe4d172a006d8ef3783ae745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=27=C3=A9lectron=20rare?= <108685187+electron-rare@users.noreply.github.com> Date: Sun, 5 Apr 2026 20:00:26 +0200 Subject: [PATCH] Fix Constant startup and remote cockpit attach --- constant/cockpit.py | 7 +-- constant/memory.py | 38 ++++++++++++++ constant/tui.py | 4 +- scripts/constant-tmux-remote-window.sh | 71 +++++++++++++++++++++----- 4 files changed, 103 insertions(+), 17 deletions(-) diff --git a/constant/cockpit.py b/constant/cockpit.py index 6374904..358cf54 100644 --- a/constant/cockpit.py +++ b/constant/cockpit.py @@ -34,11 +34,12 @@ def _tmux_list_command(session_name: str) -> list[str]: def _ssh_command(target: str, inner: str) -> list[str]: - prefixed = ( - 'export PATH="$HOME/.local/bin:$HOME/.npm-global/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"; ' + remote_shell = ( + 'PATH="$HOME/.local/bin:$HOME/.npm-global/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"; ' + "export PATH; " f"{inner}" ) - return ["ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=1", target, "bash", "--noprofile", "--norc", "-lc", prefixed] + return ["ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=1", target, remote_shell] def _machine_session_name(session_name: str, machine_label: str) -> str: diff --git a/constant/memory.py b/constant/memory.py index 562a1b8..8d4aa24 100644 --- a/constant/memory.py +++ b/constant/memory.py @@ -722,6 +722,44 @@ def rebuild_workspace_memory(workspace: str | Path, enroll: bool = True) -> dict connection.close() +def prime_workspace_memory(workspace: str | Path, enroll: bool = True) -> dict[str, Any]: + workspace_path = _normalize_workspace(workspace) + config = load_memory_config() + if enroll and str(workspace_path) not in config["workspace_enrollments"]: + config["workspace_enrollments"].append(str(workspace_path)) + config["workspace_enrollments"] = sorted(set(config["workspace_enrollments"])) + save_memory_config(config) + + connection = _connect() + try: + _upsert_workspace(connection, workspace_path) + row = connection.execute( + """ + select count(*) as document_count, max(updated_at) as last_document_at + from documents + where workspace = ? + """, + (str(workspace_path),), + ).fetchone() + workspace_row = connection.execute( + "select repo_root, last_indexed_at from workspaces where path = ?", + (str(workspace_path),), + ).fetchone() + connection.commit() + return { + "workspace": str(workspace_path), + "repo_root": str(workspace_row["repo_root"]) if workspace_row else str(_detect_repo_root(workspace_path)), + "enrolled": str(workspace_path) in load_memory_config()["workspace_enrollments"], + "documents": int(row["document_count"] or 0) if row else 0, + "last_document_at": row["last_document_at"] if row else None, + "last_indexed_at": workspace_row["last_indexed_at"] if workspace_row else None, + "store_path": str(memory_store_path()), + "mode": "prime", + } + finally: + connection.close() + + def memory_status(workspace: str | None = None) -> dict[str, Any]: connection = _connect() try: diff --git a/constant/tui.py b/constant/tui.py index 86fc05b..292f45b 100644 --- a/constant/tui.py +++ b/constant/tui.py @@ -9,7 +9,7 @@ from typing import Any from .cockpit import ROLES, capture_pane, focus_machine, restart_pane, runtime_status from .daemon import request as daemon_request -from .memory import list_decisions, memory_status, persona_markdown, rebuild_workspace_memory, summarize_mission +from .memory import list_decisions, memory_status, persona_markdown, prime_workspace_memory, rebuild_workspace_memory, summarize_mission from .state import list_missions, load_fleet_config, mission_events_file @@ -621,7 +621,7 @@ def _run(stdscr: Any, workspace: str, local_session: str, machine_session: str) def run_tui(workspace: str, local_session: str = "constant-fleet", machine_session: str = "constant") -> dict[str, Any] | None: try: - rebuild_workspace_memory(workspace, enroll=True) + prime_workspace_memory(workspace, enroll=True) except Exception: pass return curses.wrapper(lambda stdscr: _run(stdscr, str(Path(workspace).expanduser().resolve()), local_session, machine_session)) diff --git a/scripts/constant-tmux-remote-window.sh b/scripts/constant-tmux-remote-window.sh index 6e5fcc0..f27cc46 100755 --- a/scripts/constant-tmux-remote-window.sh +++ b/scripts/constant-tmux-remote-window.sh @@ -103,6 +103,9 @@ done if zellij_ai_is_local_target "$target"; then local_repo_dir="$(zellij_ai_expand_home_path "$repo_dir")" local_workspace="$(zellij_ai_expand_home_path "$workspace")" + if [[ ! -d "$local_workspace" ]]; then + local_workspace="$local_repo_dir" + fi launcher="$local_repo_dir/scripts/constant-machine.sh" args=( --workspace "$local_workspace" @@ -119,18 +122,62 @@ if zellij_ai_is_local_target "$target"; then exec env ZELLIJ_AI_MACHINE_NAME="$label" "$launcher" "${args[@]}" fi -printf -v remote_inner 'repo_dir=%q; workspace=%q; session_name=%q; repo_dir="${repo_dir#\\}"; workspace="${workspace#\\}"; case "$repo_dir" in \$HOME|\$HOME/*) repo_dir="${HOME}${repo_dir#\$HOME}" ;; "~") repo_dir="$HOME" ;; ~/*) repo_dir="$HOME/${repo_dir#~/}" ;; esac; case "$workspace" in \$HOME|\$HOME/*) workspace="${HOME}${workspace#\$HOME}" ;; "~") workspace="$HOME" ;; ~/*) workspace="$HOME/${workspace#~/}" ;; esac; launcher="$repo_dir/scripts/constant-machine.sh"; if [[ ! -x "$launcher" ]]; then launcher="$repo_dir/scripts/constant-tmux-machine.sh"; fi; args=(--workspace "$workspace" --session "$session_name" --repo-dir "$repo_dir");' \ - "$repo_dir" \ - "$workspace" \ - "$session" - -if [[ -n "$claude_config_dir" ]]; then - printf -v remote_inner '%s args+=(--claude-config-dir %q);' "$remote_inner" "$claude_config_dir" -fi - +printf -v repo_dir_q '%q' "$repo_dir" +printf -v workspace_q '%q' "$workspace" +printf -v session_q '%q' "$session" +printf -v claude_config_q '%q' "$claude_config_dir" +printf -v label_q '%q' "$label" +recreate_literal=false if $recreate; then - printf -v remote_inner '%s args+=(--recreate);' "$remote_inner" + recreate_literal=true fi -printf -v remote_inner '%s exec env ZELLIJ_AI_MACHINE_NAME=%q ZELLIJ_AI_FORCE_OSC52=true "$launcher" "${args[@]}"' "$remote_inner" "$label" -exec ssh -tt "$target" bash -lc "$remote_inner" +read -r -d '' remote_shell <