Merge branch 'agentydragon-35-tui-inspect-env-integration' into agentydragon

This commit is contained in:
Rai (Michael Pokorny)
2025-06-25 00:20:50 -07:00
4 changed files with 64 additions and 4 deletions

View File

@@ -10,7 +10,7 @@ last_updated = "2025-06-25T04:45:29Z"
## Status
**General Status**: Not started
**General Status**: Done
**Summary**: Follow-up to Task 10; add slash-command and TUI bindings for `inspect-env`.
## Goal
@@ -27,11 +27,15 @@ Add an `/inspect-env` slash-command in the TUI that invokes the existing `codex
## Implementation
**How it was implemented**
*(Not implemented yet)*
**High-level approach**
- Extend `SlashCommand` enum with `InspectEnv` and provide user-visible description.
- Add `InlineInspectEnv` variant to `AppEvent` enum to represent inline slash-command invocation.
- Update dispatch logic in `App::run` to spawn a background thread on `InlineInspectEnv` that runs `codex inspect-env`, reads its stdout line-by-line, and sends each line as `AppEvent::LatestLog`, then triggers a redraw.
- Wire up `/inspect-env` to dispatch `InlineInspectEnv` in the slash-command handling.
- Add unit tests in the TUI crate to verify `built_in_slash_commands()` includes `inspect-env` mapping and description.
**How it works**
*(Not implemented yet)*
When the user enters `/inspect-env`, the TUI parser recognizes the command and emits `AppEvent::InlineInspectEnv`. The main event loop handles this event by spawning a thread that invokes the external `codex inspect-env` command, captures its output line-by-line, and forwards each line into the TUI log pane via `AppEvent::LatestLog`. A redraw is scheduled once the inspection completes.
## Notes

View File

@@ -24,6 +24,10 @@ use std::time::Instant;
use codex_core::ResponseItem;
use uuid::Uuid;
use std::io::{BufRead, BufReader};
use std::process::{Command, Stdio};
use std::thread;
/// Top-level application state: which full-screen view is currently active.
#[allow(clippy::large_enum_variant)]
enum AppState<'a> {
@@ -325,6 +329,32 @@ impl<'a> App<'a> {
}
self.app_event_tx.send(AppEvent::Redraw);
}
AppEvent::InlineInspectEnv(_raw) => {
let tx = self.app_event_tx.clone();
thread::spawn(move || {
match Command::new("codex")
.arg("inspect-env")
.stdout(Stdio::piped())
.spawn()
{
Ok(mut child) => {
if let Some(stdout) = child.stdout.take() {
let reader = BufReader::new(stdout);
for line in reader.lines().flatten() {
let _ = tx.send(AppEvent::LatestLog(line));
}
}
let _ = child.wait();
}
Err(err) => {
let _ = tx.send(AppEvent::LatestLog(
format!("Failed to spawn inspect-env: {err}")
));
}
}
let _ = tx.send(AppEvent::Redraw);
});
}
AppEvent::MountAdd { host, container, mode } => {
if let Err(err) = do_mount_add(&mut self.config, &host, &container, &mode) {
tracing::error!("mount-add failed: {err}");
@@ -447,6 +477,10 @@ impl<'a> App<'a> {
self.app_event_tx.send(AppEvent::Redraw);
}
}
SlashCommand::InspectEnv => {
let _ = self.app_event_tx.send(AppEvent::InlineInspectEnv(String::new()));
let _ = self.app_event_tx.send(AppEvent::Redraw);
}
},
}
}

View File

@@ -34,6 +34,8 @@ pub(crate) enum AppEvent {
InlineMountAdd(String),
/// Inline mount-remove DSL: raw argument string (`container=...`).
InlineMountRemove(String),
/// Inline inspect-env DSL: raw argument string (unused).
InlineInspectEnv(String),
/// Perform mount-add: create symlink and update sandbox policy.
MountAdd {
host: std::path::PathBuf,

View File

@@ -21,6 +21,8 @@ pub enum SlashCommand {
MountAdd,
/// Remove a dynamic mount by container path.
MountRemove,
/// Inspect sandbox and container environment (mounts, permissions, network).
InspectEnv,
}
impl SlashCommand {
@@ -35,6 +37,7 @@ impl SlashCommand {
SlashCommand::Quit => "Exit the application.",
SlashCommand::MountAdd => "Add a mount: host path → container path.",
SlashCommand::MountRemove => "Remove a mount by container path.",
SlashCommand::InspectEnv => "Inspect sandbox and container environment (mounts, permissions, network)",
}
}
@@ -49,3 +52,20 @@ impl SlashCommand {
pub fn built_in_slash_commands() -> HashMap<&'static str, SlashCommand> {
SlashCommand::iter().map(|c| (c.command(), c)).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn built_in_includes_inspect_env() {
let commands = built_in_slash_commands();
assert_eq!(commands.get("inspect-env"), Some(&SlashCommand::InspectEnv));
}
#[test]
fn inspect_env_description_contains_keyword() {
let desc = SlashCommand::InspectEnv.description();
assert!(desc.contains("sandbox"), "description was: {}", desc);
}
}