#!/usr/bin/env python3
"""Py Agent [v0.9.9.64] - Main CLI Runtime, Workspace Agent & Command Dispatcher [CR.01 CO.01]"""
import json
import os
import re
import shutil
import sqlite3
import subprocess
import sys
import tempfile
import time
from contextlib import closing
CFG_DIR: str = os.path.expanduser("~/.config/py-agent")
VAR_DIR: str = os.path.join(CFG_DIR, "var")
os.makedirs(VAR_DIR, exist_ok=True)
CONTEXT_FILE: str = os.path.join(CFG_DIR, "ai-context.md")
SKILLS_DIR: str = os.path.join(CFG_DIR, "skills")
SESSIONS_DIR: str = os.path.join(CFG_DIR, "projects", ".database")
BASE_PROMPT_CHAT: str = "Active, natural conversational assistant."
BASE_PROMPT_AGENT: str = "Active local workspace developer agent."
# Precompiled hot-path regular expressions
RE_THINK_TAGS: re.Pattern = re.compile(r"[\s\S]*?(?:|$)", re.DOTALL)
RE_AUTO_RUN: re.Pattern = re.compile(
r"Run:\s*((?:trace symbol|blast radius|read function|find symbol)\s+[a-zA-Z0-9_.:#/-]+|architecture overview)"
)
# Requires '/' prefix for single letters (a, b, f) so plain words are not swallowed
RE_THINK_BIN: re.Pattern = re.compile(r"^(?:/(?:[fba]|tk)|/?tk)(?:\s+(\d+))?$", re.IGNORECASE)
RE_SHELL_META: re.Pattern = re.compile(r"[\[\]{}()='\",;|#<>]")
def load_env_file(path: str) -> None:
"""Auto-publishes .env from .env.example and loads variables into os.environ."""
if not os.path.isfile(path):
example_p = os.path.join(os.path.dirname(path), ".env.example")
if os.path.isfile(example_p):
try:
shutil.copy2(example_p, path)
except OSError:
pass
if os.path.isfile(path):
try:
with open(path, "r", encoding="utf-8") as f:
for line in f:
if (l := line.strip()) and not l.startswith("#") and "=" in l:
k, v = l.replace("export ", "", 1).split("=", 1)
if (k := k.strip()) and k not in os.environ:
os.environ[k] = v.split(" #")[0].strip().strip('"').strip("'")
except OSError:
pass
load_env_file(os.path.join(CFG_DIR, ".env"))
if (mod_dir := os.path.join(CFG_DIR, "modules")) not in sys.path:
sys.path.append(mod_dir)
try:
import agent_context as context
from agent_context import STOP_WORDS
except ImportError as e:
sys.stderr.write(f"\033[1;31m[CRITICAL]: Failed to load agent_context: {e}\033[0m\n")
sys.exit(1)
# Lazy references to heavy interactive modules to preserve sub-millisecond shell-hook startup
commands = None
core = None
memories = None
sessions = None
skills = None
tts = None
ui = None
voice = None
def _load_agent_modules() -> None:
"""Loads interactive modules on demand, keeping shell hook invocations sub-millisecond."""
global commands, core, memories, sessions, skills, tts, ui, voice
if commands is not None:
return
try:
import agent_commands as _commands
import agent_core as _core
import agent_memories as _memories
import agent_sessions as _sessions
import agent_skills as _skills
import agent_tts as _tts
import agent_ui as _ui
import agent_voice as _voice
commands, core, memories, sessions, skills, tts, ui, voice = (
_commands,
_core,
_memories,
_sessions,
_skills,
_tts,
_ui,
_voice,
)
except ImportError as e:
sys.stderr.write(f"\033[1;31m[CRITICAL]: Failed to load modules: {e}\033[0m\n")
sys.exit(1)
try:
import readline
readline.parse_and_bind(r'"\e[A": previous-history')
readline.parse_and_bind(r'"\e[B": next-history')
except ImportError:
pass
_transient_cmd_count: int = 0
def _show_transient_status(tag: str) -> None:
"""Displays an inline status tag with breathing room, accumulating for batch removal on next query."""
global _transient_cmd_count
sys.stdout.write(f" \033[2m[{tag}]\033[0m\n\n")
sys.stdout.flush()
_transient_cmd_count += 3
_flash_status = _show_transient_status
def _clear_transient_status(query: str = "") -> None:
"""Erases the ephemeral slash command block and repositions the real query prompt with breathing room."""
global _transient_cmd_count
if _transient_cmd_count > 0:
try:
if query:
sys.stdout.write(f"\033[{_transient_cmd_count + 1}A\r\x1b[0J❯ {query}\n")
else:
sys.stdout.write(f"\033[{_transient_cmd_count}A\r\x1b[0J")
sys.stdout.flush()
except OSError:
pass
_transient_cmd_count = 0
def workspace_db_counts(safe_name: str, workspace_path: str = "") -> tuple[int, int]:
"""Retrieves SQLite turn counts and OKF memory counts without spawning empty databases."""
_load_agent_modules()
db_path = os.path.join(SESSIONS_DIR, f"{safe_name}.db")
t = 0
if os.path.isfile(db_path):
try:
with closing(sqlite3.connect(f"file:{db_path}?mode=ro", uri=True, timeout=1.5)) as conn:
try:
t = conn.execute("SELECT COUNT(*) FROM turns WHERE workspace = ?", (safe_name,)).fetchone()[0]
except (sqlite3.Error, TypeError, IndexError):
t = 0
except sqlite3.Error:
t = 0
m_count = memories.get_memory_count(workspace_path) if workspace_path else 0
return t, m_count
def ensure_clean_agent_dir(workspace_path: str) -> None:
"""Moves root artifacts into .agent/ without clobbering existing destinations."""
if not (ws_name := os.path.basename(workspace_path)):
return
agent_dir = os.path.join(workspace_path, ".agent")
try:
os.makedirs(os.path.join(agent_dir, "memory"), exist_ok=True)
except OSError:
return
targets = [
f"index-map-{ws_name}.txt",
f"index-map-memory-{ws_name}.db",
f"index-map-memory-{ws_name}.db-wal",
f"index-map-memory-{ws_name}.db-shm",
"history.md",
]
for fname in targets:
src = os.path.join(workspace_path, fname)
dst = os.path.join(agent_dir, fname)
if os.path.exists(src):
if not os.path.exists(dst):
try:
os.replace(src, dst)
except OSError:
pass
elif fname == "history.md":
try:
with open(src, "r", encoding="utf-8") as sf, open(dst, "a", encoding="utf-8") as df:
df.write(f"\n\n{sf.read().strip()}\n")
os.remove(src)
except OSError:
pass
def _sweep_dead_session_locks() -> None:
"""Removes .session lockfiles whose owning process is dead. Ignores alive processes."""
sess_dir = os.path.join(CFG_DIR, "var", ".active_sessions")
if not os.path.isdir(sess_dir):
return
try:
entries = os.listdir(sess_dir)
except OSError:
return
if not entries:
return
for f in entries:
if not f.endswith(".session"):
continue
try:
pid = int(f.rsplit("-", 1)[-1].replace(".session", ""))
if pid <= 0:
continue
os.kill(pid, 0)
except ProcessLookupError:
try:
os.remove(os.path.join(sess_dir, f))
except OSError:
pass
except (PermissionError, ValueError, OSError):
pass
def clean_exit(safe_name: str | None = None, workspace_path: str | None = None, sub_id: int | None = None) -> None:
"""Cleans active session locks and worktrees independently, then exits cleanly."""
_clear_transient_status("")
_load_agent_modules()
if safe_name:
if sub_id is not None and sub_id > 0 and workspace_path:
try:
merge = False
if ui and hasattr(ui, "confirm_tool"):
merge = ui.confirm_tool(f"Merge sub-agent #{sub_id} worktree into main branch")
sessions.cleanup_subagent_worktree(workspace_path, sub_id, merge=merge)
except Exception as e:
if os.environ.get("AI_DEBUG") == "1":
sys.stderr.write(f"\r\n[debug] Worktree cleanup failed for sub-agent #{sub_id}: {e}\r\n")
try:
sessions.cleanup_sub_agent(safe_name, os.getpid())
except Exception as e:
if os.environ.get("AI_DEBUG") == "1":
sys.stderr.write(f"\r\n[debug] Session lock cleanup failed: {e}\r\n")
_sweep_dead_session_locks()
if ui and hasattr(ui, "_console"):
ui._console.print("[yellow]Exiting session.[/yellow]")
else:
print("Exiting session.")
sys.exit(0)
def _launch_surface(
script_path: str, is_agent: bool, ws_path: str, skill: str, history: list, args: list[str] | None = None
) -> None:
"""Launches secondary surfaces (TUI, PyCode, WebUI) with state continuity and E2BIG protection."""
_load_agent_modules()
if not os.path.exists(script_path):
ui._console.print(f"[red][sys] Launcher script not found: {script_path}[/red]\n")
return
hist_file = None
try:
hist_data = json.dumps(history)
if len(hist_data) > 65536:
with tempfile.NamedTemporaryFile(
"w", delete=False, prefix="ai_hist_", suffix=".json", encoding="utf-8"
) as tf:
tf.write(hist_data)
hist_file = tf.name
except Exception:
hist_data = ""
env = {
**os.environ,
"AI_IS_AGENT": "1" if is_agent else "0",
"AI_WORKSPACE_PATH": ws_path,
"AI_ACTIVE_SKILL": skill,
"AI_CONFIRM_GATES": os.environ.get("AI_CONFIRM_GATES", "1"),
}
if hist_file:
env["AI_SESSION_HISTORY_FILE"] = hist_file
env["AI_SESSION_HISTORY"] = ""
else:
env["AI_SESSION_HISTORY"] = hist_data
cmd = ([sys.executable, script_path] if script_path.endswith(".py") else ["/bin/bash", script_path]) + (args or [])
try:
proc = subprocess.run(cmd, env=env)
if proc.returncode != 0 and proc.returncode != 130:
ui._console.print(f"[dim yellow][sys] Surface exited with rc={proc.returncode}.[/dim yellow]\n")
st = core.get_state()
os.environ["AI_SHOW_THINKING"] = "1" if st.get("show_thinking", True) else "0"
ui._console.print("[green][sys] Resumed CLI session.[/green]\n")
except Exception as e:
ui._console.print(f"[red][sys] Launch failed: {e}[/red]\n")
finally:
if hist_file and os.path.exists(hist_file):
try:
os.remove(hist_file)
except OSError:
pass
def resolve_default_profile(is_agent: bool = True) -> str:
"""Dynamically resolves default profile from environment, active model, or available profiles."""
if not is_agent:
return "chat"
if env_skill := os.environ.get("AI_ACTIVE_SKILL", "").strip():
primary = env_skill.split()[0].lstrip("-").lower()
if primary and primary != "init":
return primary
try:
m_name = ""
if ui and hasattr(ui, "get_local_model_name"):
m_name = ui.get_local_model_name(max_age=5.0).lower()
if m_name and "not loaded" not in m_name and "offline" not in m_name:
prof_dir = os.path.join(SKILLS_DIR, "profiles")
if os.path.isdir(prof_dir):
available = [os.path.splitext(f)[0] for f in os.listdir(prof_dir) if f.endswith(".md")]
clean_m = re.sub(r"[^a-z0-9]", "", m_name)
for p in available:
clean_p = re.sub(r"[^a-z0-9]", "", p)
if clean_p in clean_m or clean_m in clean_p:
return p
except Exception:
pass
return "agent"
def run_interactive_chat(args: list[str]) -> None:
"""Primary interactive agent and chat engine."""
_load_agent_modules()
is_agent = args[0] == "--talk-chat"
raw_ws = os.environ.get("AI_WORKSPACE_PATH", os.getcwd())
clean_ws = re.sub(r"^[\s]+", "", str(raw_ws)).strip().splitlines()[0].strip() if raw_ws else os.getcwd()
original_workspace_path = os.path.realpath(os.path.expanduser(clean_ws))
workspace_path = original_workspace_path
os.environ["AI_WORKSPACE_PATH"] = workspace_path
home_dir = os.path.expanduser("~")
safe_name = core.workspace_safe_name(workspace_path, home_dir)
try:
os.chdir(workspace_path)
except OSError:
pass
ensure_clean_agent_dir(workspace_path)
cfg_file = os.path.join(workspace_path, ".agent", "config.json")
selected_profile = resolve_default_profile(is_agent)
is_yolo, use_map, is_py, memory_active, adapters_active = False, False, False, False, False
if is_agent:
if not os.path.exists(cfg_file):
selected_profile, is_yolo, use_map, is_py, memory_active, adapters_active = ui.select_workspace_profile(
os.path.basename(workspace_path)
)
try:
os.makedirs(os.path.dirname(cfg_file), exist_ok=True)
with open(cfg_file, "w", encoding="utf-8") as cf:
json.dump(
{
"profile": selected_profile,
"yolo": is_yolo,
"map": use_map,
"py": is_py,
"memory": memory_active,
"adapters": adapters_active,
"created_at": time.strftime("%Y-%m-%d %H:%M"),
},
cf,
indent=2,
)
except OSError:
pass
else:
try:
with open(cfg_file, "r", encoding="utf-8") as cf:
d = json.load(cf)
selected_profile = d.get("profile") or resolve_default_profile(is_agent)
is_yolo = bool(d.get("yolo", False))
use_map = bool(d.get("map", False))
is_py = bool(d.get("py", False))
memory_active = bool(d.get("memory", False))
adapters_active = bool(d.get("adapters", d.get("adp", False)))
if "calm" in d:
core.save_state("calm_mode", bool(d["calm"]))
except (OSError, json.JSONDecodeError):
pass
profile_args = set()
for arg in args:
if arg.startswith("-") and arg not in ("--talk", "--talk-chat"):
selected_profile = arg.lstrip("-").lower()
profile_args.add(arg)
if is_agent:
clean_name = resolve_default_profile(True) if selected_profile in ("init", "pi/pro") else selected_profile
profile_content = skills.load_skill_content(clean_name, SKILLS_DIR, CFG_DIR)
if not profile_content and clean_name not in ("agent", "init"):
ui._console.print(
f"[dim yellow][sys] Skill '{clean_name}' not found. Using minimal agent prompt.[/dim yellow]"
)
active_system_prompt = profile_content or BASE_PROMPT_AGENT
os.environ["AI_ACTIVE_SKILL"] = clean_name
st_init = core.get_state()
reasoning_budget = st_init.get("reasoning_budget", 500)
reasoning_active = st_init.get("reasoning_active", reasoning_budget > 0)
if os.path.isfile(cfg_file):
try:
with open(cfg_file, "r", encoding="utf-8") as cf:
d = json.load(cf)
if "reasoning" in d:
reasoning_active = bool(d["reasoning"])
core.save_state("reasoning_active", reasoning_active)
if "reasoning_budget" in d:
reasoning_budget = int(d["reasoning_budget"])
core.save_state("reasoning_budget", reasoning_budget)
except Exception:
pass
is_purebash = clean_name in ("purebash", "swe", "bash") or "purebash" in clean_name
core.save_state("use_map", use_map)
core.save_state("yolo_mode", is_yolo)
core.save_state("ipython_mode", is_py)
core.save_state("memory_active", memory_active)
core.save_state("adapters_active", adapters_active)
core.save_state("purebash_mode", is_purebash)
os.environ["AI_USE_MAP"] = "1" if use_map else "0"
os.environ["AI_IPYTHON_MODE"] = "1" if is_py else "0"
os.environ["AI_PUREBASH_MODE"] = "1" if is_purebash else "0"
if is_yolo:
os.environ["AI_CONFIRM_GATES"] = "0"
if use_map:
agent_dir = os.path.join(workspace_path, ".agent")
ws_name = os.path.basename(workspace_path)
txt_p = os.path.join(agent_dir, f"index-map-{ws_name}.txt")
db_p = os.path.join(agent_dir, f"index-map-memory-{ws_name}.db")
imap_bin = os.path.join(CFG_DIR, "tools", "index-map", "index-map")
if not os.path.isfile(txt_p) or not os.path.isfile(db_p):
if os.path.isfile(imap_bin):
res_map = subprocess.run(
[sys.executable, imap_bin, "--agent", workspace_path], capture_output=True, text=True
)
if res_map.returncode == 0:
ui._console.print("[dim][sys] Map enabled: compiled index-map.[/dim]")
else:
ui._console.print(
f"[dim yellow][sys] Map compilation failed (rc={res_map.returncode}): {res_map.stderr.strip()[:100]}[/dim yellow]"
)
map_path = next(
(p for p in (txt_p, os.path.join(workspace_path, f"index-map-{ws_name}.txt")) if os.path.exists(p)),
None,
)
if map_path:
try:
with open(map_path, "r", encoding="utf-8") as mf:
new_map = mf.read().strip()
if "### CODESPACE MAP:" not in active_system_prompt:
active_system_prompt += f"\n\n### CODESPACE MAP:\n{new_map}"
except OSError:
pass
else:
clean_name = selected_profile if (selected_profile and selected_profile != "pi/pro") else "chat"
skill_content = skills.load_skill_content(clean_name, SKILLS_DIR, CFG_DIR)
if not skill_content and clean_name != "chat":
ui._console.print(
f"[dim yellow][sys] Skill '{clean_name}' not found. Using minimal chat prompt.[/dim yellow]"
)
active_system_prompt = skill_content or BASE_PROMPT_CHAT
os.environ["AI_ACTIVE_SKILL"] = clean_name
inst_p = os.path.join(SKILLS_DIR, "system_instructions.md")
if os.path.isfile(inst_p) and os.path.getsize(inst_p) > 0:
try:
with open(inst_p, "r", encoding="utf-8") as f:
rules = [l for l in f if l.strip() and not l.strip().startswith("#")]
if active_rules := "".join(rules).strip():
active_system_prompt += f"\n\n{active_rules}"
except OSError:
pass
pending_args = [a for a in args[1:] if a not in profile_args]
pending_query = " ".join(pending_args) if pending_args else None
if pending_query and ("CODEBASE INDEX MAP" in pending_query or "index-map" in pending_query):
if use_map and "### CODESPACE MAP:" not in active_system_prompt:
active_system_prompt += f"\n\n### CODESPACE MAP:\n{pending_query}"
pending_query = None
chat_history = [{"role": "system", "content": active_system_prompt}]
st = core.get_state()
show_stats = st.get("show_stats", True)
if not is_agent:
memory_active = st.get("memory_active", False)
reasoning_budget = st.get("reasoning_budget", 500)
reasoning_active = st.get("reasoning_active", reasoning_budget > 0)
os.environ["AI_RENDER_MARKDOWN"] = "1" if st.get("render_markdown", False) else "0"
os.environ["AI_REASONIX_ACTIVE"] = "1" if st.get("reasonix_active", True) else "0"
os.environ["AI_SHOW_THINKING"] = "1" if st.get("show_thinking", True) else "0"
if is_yolo or st.get("yolo_mode", False):
os.environ["AI_CONFIRM_GATES"] = "0"
db_turns, mem_count = workspace_db_counts(safe_name, workspace_path) if is_agent else (0, 0)
_sweep_dead_session_locks()
sub_id = sessions.get_sub_agent_id(safe_name, os.getpid()) if is_agent else None
if sub_id is not None and sub_id > 0:
try:
new_ws = sessions.create_subagent_worktree(original_workspace_path, sub_id)
os.chdir(new_ws)
workspace_path = new_ws
os.environ["AI_WORKSPACE_PATH"] = workspace_path
except OSError as e:
ui._console.print(f"[red][sys] Sub-agent worktree setup failed: {e}[/red]")
clean_exit(safe_name, workspace_path=original_workspace_path, sub_id=sub_id)
# Wake-on-Key Resonant Ribbon Box
wake_key = ui.draw_session_box(
workspace_path,
home_dir,
is_agent,
db_turns,
mem_count,
memory_active,
active_system_prompt,
clean_name,
sub_id=sub_id,
box_style=st.get("box_style", 1),
wake_on_key=not bool(pending_query),
)
if wake_key:
try:
readline.set_startup_hook(lambda: readline.insert_text(wake_key))
except Exception:
pass
try:
while True:
if pending_query:
query, pending_query = pending_query, None
else:
try:
query = voice.get_prompt_input()
except EOFError:
break
finally:
try:
readline.set_startup_hook(None)
except Exception:
pass
if not query:
continue
ctx = commands.SessionContext(
workspace_path=workspace_path,
home_dir=home_dir,
safe_name=safe_name,
cfg_file=cfg_file,
is_agent=is_agent,
chat_history=chat_history,
active_system_prompt=active_system_prompt,
clean_name=clean_name,
use_map=use_map,
memory_active=memory_active,
is_yolo=is_yolo,
reasoning_active=reasoning_active,
reasoning_budget=reasoning_budget,
show_stats=show_stats,
flash_status=_flash_status,
launch_surface_fn=_launch_surface,
clean_exit_fn=lambda name: clean_exit(
name,
workspace_path=original_workspace_path if (workspace_path != original_workspace_path) else None,
sub_id=sub_id if is_agent else None,
),
)
handled, new_query = commands.dispatch_command(query, ctx)
use_map = ctx.use_map
memory_active = ctx.memory_active
is_yolo = ctx.is_yolo
reasoning_active = ctx.reasoning_active
reasoning_budget = ctx.reasoning_budget
show_stats = ctx.show_stats
if handled:
continue
if new_query:
query = new_query
_clear_transient_status(query)
memory_ctx = memories.get_memory_context(workspace_path) if (is_agent and memory_active) else ""
if RE_THINK_BIN.match(query):
think_bin = next(
(p for p in (f"{CFG_DIR}/modules/agent_chat.py", f"{CFG_DIR}/modules/chat") if os.path.isfile(p)),
None,
)
if think_bin:
try:
res = subprocess.run(
[sys.executable, think_bin, query],
input=json.dumps(chat_history),
text=True,
timeout=120,
)
if res.returncode != 0:
sys.stderr.write(f"\033[1;31m[Warning] chat failed: rc={res.returncode}\033[0m\n")
continue
except Exception as e:
sys.stderr.write(f"\033[1;31m[Warning] chat failed: {e}\033[0m\n")
else:
ui._console.print("[dim yellow][sys] Chat assistant helper not found.[/dim yellow]\n")
continue
sys_ctx = (
""
if query.startswith("init") and "--init" in query
else skills.get_system_context(query, CONTEXT_FILE, STOP_WORDS, SKILLS_DIR, CFG_DIR)
)
if sys_ctx == "__ABORT_TURN__":
ui._console.print("[yellow][sys] Action cancelled.[/yellow]\n")
continue
comb_ctx = "\n\n".join(filter(None, [memory_ctx, sys_ctx]))
prompt = f"\n{comb_ctx}\n\n\n{query}" if comb_ctx else query
chat_history.append({"role": "user", "content": prompt})
try:
readline.add_history(query)
except Exception:
pass
ans = core.stream_response(
chat_history,
prefix="",
show_stats=show_stats,
thinking_budget=reasoning_budget if reasoning_active else 0,
is_agent=is_agent,
)
if not ans:
# Stream failed or interrupted: roll back user turn to prevent context drift
if chat_history and chat_history[-1].get("role") == "user":
chat_history.pop()
continue
clean_ans = RE_THINK_TAGS.sub("", ans).strip()
chat_history.append({"role": "assistant", "content": clean_ans or ans})
try:
tts.speak_response(clean_ans or ans)
except Exception as e:
if os.environ.get("AI_DEBUG") == "1":
sys.stderr.write(f"\r\n[debug] TTS playback error: {e}\r\n")
if not show_stats:
print()
if is_agent:
try:
sessions.log_turn(safe_name, query, ans)
except Exception as e:
if os.environ.get("AI_DEBUG") == "1":
sys.stderr.write(f"\r\n[debug] Failed to log turn to SQLite: {e}\r\n")
if match := RE_AUTO_RUN.search(ans):
try:
readline.set_startup_hook(lambda: readline.insert_text(match.group(1).strip()))
except Exception:
pass
agent_dir = os.path.join(workspace_path, ".agent")
try:
os.makedirs(agent_dir, exist_ok=True)
hist_file = os.path.join(agent_dir, "history.md")
mode = "a" if os.path.exists(hist_file) else "w"
with open(hist_file, mode, encoding="utf-8") as hf:
if mode == "w":
hf.write(f"# Workspace History: {os.path.basename(workspace_path)}\n\n")
hf.write(
f"## [{time.strftime('%Y-%m-%d %H:%M')}] User:\n{query}\n\n### Agent:\n{ans}\n\n---\n\n"
)
except OSError as e:
if os.environ.get("AI_DEBUG") == "1":
sys.stderr.write(f"\r\n[debug] Failed to write history.md: {e}\r\n")
except KeyboardInterrupt:
clean_exit(
safe_name if is_agent else None,
workspace_path=original_workspace_path if (workspace_path != original_workspace_path) else None,
sub_id=sub_id if is_agent else None,
)
def run_direct_query(args: list[str]) -> None:
"""Executes instant single-turn CLI prompt without interactive session loop."""
_load_agent_modules()
query_parts = args[1:]
skill_content = ""
clean_parts: list[str] = []
for part in query_parts:
if part.startswith("-") and part != "-":
loaded = skills.load_skill_content(part.lstrip("-").lower(), SKILLS_DIR, CFG_DIR)
if loaded:
skill_content = loaded
continue
clean_parts.append(part)
query = " ".join(clean_parts).strip()
if query == "-" or (not query and not sys.stdin.isatty()):
try:
query = sys.stdin.read().strip()
except Exception:
query = ""
if not query:
ui._console.print("[dim yellow][sys] Usage: ai --talk [-skill][/dim yellow]")
sys.exit(0)
if query.lower() in ("/help", "/h", "help", "--help", "-h"):
ui.show_help()
sys.exit(0)
if "AI_SHOW_THINKING" not in os.environ:
os.environ["AI_SHOW_THINKING"] = "0"
thinking_budget = 0
if env_budget := os.environ.get("AI_THINKING_BUDGET"):
try:
thinking_budget = max(0, int(env_budget.strip()))
except (ValueError, TypeError):
thinking_budget = 0
sys_ctx = skills.get_system_context(query, CONTEXT_FILE, STOP_WORDS, SKILLS_DIR, CFG_DIR)
if sys_ctx == "__ABORT_TURN__":
sys_ctx = ""
active_p = skill_content or BASE_PROMPT_CHAT
inst_p = os.path.join(SKILLS_DIR, "system_instructions.md")
if os.path.isfile(inst_p) and os.path.getsize(inst_p) > 0:
try:
with open(inst_p, "r", encoding="utf-8") as f:
rules = [l for l in f if l.strip() and not l.strip().startswith("#")]
if active_rules := "".join(rules).strip():
active_p += f"\n\n{active_rules}"
except OSError:
pass
messages = [
{"role": "system", "content": active_p},
{"role": "user", "content": f"\n{sys_ctx}\n\n\n{query}" if sys_ctx else query},
]
core.stream_response(messages, prefix="", show_stats=False, thinking_budget=thinking_budget)
sys.exit(0)
def run_matching_search(args: list[str]) -> None:
"""Sub-millisecond shell intent matcher for command not found hook (zero eager module imports)."""
user_input = re.sub(r"[`$]", "", " ".join(args)).strip()
if not user_input or args[0].startswith("--"):
sys.exit(0)
if user_input.lower() in ("/help", "/h"):
_load_agent_modules()
ui.show_help()
sys.exit(0)
shell_name = os.path.basename(os.environ.get("SHELL", "/bin/bash"))
err_msg = f"{'zsh' if 'zsh' in shell_name else 'bash'}: {f'command not found: {user_input}' if 'zsh' in shell_name else f'{user_input}: command not found'}\n"
if RE_SHELL_META.search(user_input):
sys.stderr.write(err_msg)
sys.exit(127)
if matched := context.jaccard_search(user_input, CONTEXT_FILE, STOP_WORDS):
print(
"\n".join(
f"{l.split('|||', 1)[0]}|||{context.clean_tool_prefix(l.split('|||', 1)[1])}"
for l in matched.split("\n")
)
)
sys.exit(0)
sys.stderr.write(err_msg)
sys.exit(127)
def main() -> None:
try:
args = sys.argv[1:]
if not args:
run_direct_query(["--talk"])
elif args[0] == "--interactive" and len(args) >= 2:
_load_agent_modules()
shell_name = os.path.basename(os.environ.get("SHELL", ""))
ui.run_interactive_selection(
" ".join(args[1:]),
lambda q: context.jaccard_search(q, CONTEXT_FILE, STOP_WORDS),
context.clean_tool_prefix,
lambda n: sys.stderr.write(
f"zsh: command not found: {n}\n" if "zsh" in shell_name else f"bash: {n}: command not found\n"
),
lambda: skills.ensure_mysys_exists(SKILLS_DIR, CFG_DIR),
)
sys.exit(0)
elif args[0] in ("--talk", "--talk-chat"):
run_interactive_chat(args) if (args[0] == "--talk-chat" or len(args) == 1) else run_direct_query(args)
sys.exit(0)
else:
run_matching_search(args)
except KeyboardInterrupt:
sys.stderr.write("\nCancelled.\n")
sys.exit(130)
if __name__ == "__main__":
main()