feat: enhance workspace management and history tracking in application state
This commit is contained in:
@@ -313,6 +313,7 @@ pub fn trigger_review(state: &mut AppStateRest) -> anyhow::Result<()> {
|
|||||||
);
|
);
|
||||||
let mut ctx = build_subagent_context(def);
|
let mut ctx = build_subagent_context(def);
|
||||||
ctx.session_dir = state.session_dir.clone();
|
ctx.session_dir = state.session_dir.clone();
|
||||||
|
ctx.workspaces = state.workspace_roots.clone();
|
||||||
let probe_result = probe_build_test(
|
let probe_result = probe_build_test(
|
||||||
&state.workspace_roots,
|
&state.workspace_roots,
|
||||||
state.settings.verify_command.as_deref(),
|
state.settings.verify_command.as_deref(),
|
||||||
|
|||||||
@@ -608,6 +608,9 @@ pub fn apply_action(state: &mut AppStateRest, action: Action) {
|
|||||||
format!("Starting workflow: {}…", &script.chars().take(40).collect::<String>()),
|
format!("Starting workflow: {}…", &script.chars().take(40).collect::<String>()),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let session_dir = state.session_dir.clone();
|
||||||
|
let workspace_roots = state.workspace_roots.clone();
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -654,7 +657,9 @@ pub fn apply_action(state: &mut AppStateRest, action: Action) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let args: HashMap<String, String> = HashMap::new();
|
let args: HashMap<String, String> = HashMap::new();
|
||||||
let result = crate::app::workflow::engine::run_workflow_tracked(&wf, &args, Some(live));
|
let result = crate::app::workflow::engine::run_workflow_tracked(
|
||||||
|
&wf, &args, Some(live), &session_dir, &workspace_roots,
|
||||||
|
);
|
||||||
|
|
||||||
let (kind, message) = match result {
|
let (kind, message) = match result {
|
||||||
Ok(summary) => ("workflow_done".to_string(), summary),
|
Ok(summary) => ("workflow_done".to_string(), summary),
|
||||||
|
|||||||
+15
-5
@@ -72,6 +72,7 @@ pub struct InputState {
|
|||||||
pub autocomplete_candidates: Vec<String>,
|
pub autocomplete_candidates: Vec<String>,
|
||||||
pub autocomplete_idx: usize,
|
pub autocomplete_idx: usize,
|
||||||
pub autocomplete_visible: bool,
|
pub autocomplete_visible: bool,
|
||||||
|
pub history_file: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const COMMANDS: &[&str] = &[
|
const COMMANDS: &[&str] = &[
|
||||||
@@ -110,6 +111,7 @@ impl InputState {
|
|||||||
autocomplete_candidates: Vec::new(),
|
autocomplete_candidates: Vec::new(),
|
||||||
autocomplete_idx: 0,
|
autocomplete_idx: 0,
|
||||||
autocomplete_visible: false,
|
autocomplete_visible: false,
|
||||||
|
history_file: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,14 +221,22 @@ impl InputState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Submit the current buffer: push it to history, clear the buffer,
|
|
||||||
/// and return the submitted text.
|
|
||||||
///
|
|
||||||
/// Return: the text that was in the buffer before clearing.
|
|
||||||
pub fn submit(&mut self) -> String {
|
pub fn submit(&mut self) -> String {
|
||||||
let result = self.buffer.clone();
|
let result = self.buffer.clone();
|
||||||
if !result.is_empty() {
|
if !result.is_empty() {
|
||||||
self.history.push(result.clone());
|
if self.history.last() != Some(&result) {
|
||||||
|
self.history.push(result.clone());
|
||||||
|
if let Some(ref path) = self.history_file {
|
||||||
|
if let Ok(mut file) = std::fs::OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(path)
|
||||||
|
{
|
||||||
|
use std::io::Write;
|
||||||
|
let _ = writeln!(file, "{}", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
self.history_idx = None;
|
self.history_idx = None;
|
||||||
}
|
}
|
||||||
self.buffer.clear();
|
self.buffer.clear();
|
||||||
|
|||||||
+29
-1
@@ -104,7 +104,7 @@ impl AppStateRest {
|
|||||||
tracing::warn!("[state] session_dir has no file_name component, using empty session_id");
|
tracing::warn!("[state] session_dir has no file_name component, using empty session_id");
|
||||||
String::new()
|
String::new()
|
||||||
});
|
});
|
||||||
let state = AppStateRest {
|
let mut state = AppStateRest {
|
||||||
|
|
||||||
settings,
|
settings,
|
||||||
app_config,
|
app_config,
|
||||||
@@ -133,6 +133,34 @@ impl AppStateRest {
|
|||||||
quit: false,
|
quit: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Load project-specific history
|
||||||
|
let base_dir = state.memory_dir.parent().unwrap_or(&state.memory_dir);
|
||||||
|
if let Some(root) = state.workspace_roots.first() {
|
||||||
|
if let Ok(abs_root) = std::fs::canonicalize(root) {
|
||||||
|
use sha2::Digest;
|
||||||
|
let mut hasher = sha2::Sha256::new();
|
||||||
|
hasher.update(abs_root.to_string_lossy().as_bytes());
|
||||||
|
let hash_hex = format!("{:x}", hasher.finalize());
|
||||||
|
let folder_name = abs_root.file_name()
|
||||||
|
.map(|n| n.to_string_lossy().to_string())
|
||||||
|
.unwrap_or_else(|| "root".to_string());
|
||||||
|
let history_filename = format!("{}-{}.txt", folder_name, &hash_hex[..8]);
|
||||||
|
let history_dir = base_dir.join("history");
|
||||||
|
let _ = std::fs::create_dir_all(&history_dir);
|
||||||
|
let history_file = history_dir.join(history_filename);
|
||||||
|
|
||||||
|
if let Ok(content) = std::fs::read_to_string(&history_file) {
|
||||||
|
let history: Vec<String> = content
|
||||||
|
.lines()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.collect();
|
||||||
|
state.input.history = history;
|
||||||
|
}
|
||||||
|
state.input.history_file = Some(history_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fire-and-forget background LSP provisioning.
|
// Fire-and-forget background LSP provisioning.
|
||||||
//
|
//
|
||||||
// Flow: spawn OS thread -> provision_all() probes/installs every
|
// Flow: spawn OS thread -> provision_all() probes/installs every
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ pub struct SubagentContext {
|
|||||||
pub allowed_tools: Vec<String>,
|
pub allowed_tools: Vec<String>,
|
||||||
pub max_steps: usize,
|
pub max_steps: usize,
|
||||||
pub session_dir: PathBuf,
|
pub session_dir: PathBuf,
|
||||||
|
pub workspaces: Vec<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a `SubagentContext` from an `AgentDefinition`.
|
/// Build a `SubagentContext` from an `AgentDefinition`.
|
||||||
@@ -23,8 +24,8 @@ pub struct SubagentContext {
|
|||||||
/// fall back to an empty list (i.e. "all tools allowed") for other roles.
|
/// fall back to an empty list (i.e. "all tools allowed") for other roles.
|
||||||
/// `max_steps` is read from the definition, defaulting to 25 if absent.
|
/// `max_steps` is read from the definition, defaulting to 25 if absent.
|
||||||
///
|
///
|
||||||
/// Return: a context with empty `system_prompt` and `session_dir`,
|
/// Return: a context with empty `system_prompt`, empty `workspaces`,
|
||||||
/// resolved `max_steps`, and the resolved allowed-tool list.
|
/// empty `session_dir`, resolved `max_steps`, and the resolved allowed-tool list.
|
||||||
pub fn build_subagent_context(def: AgentDefinition) -> SubagentContext {
|
pub fn build_subagent_context(def: AgentDefinition) -> SubagentContext {
|
||||||
let allowed_tools = def.allowed_tools.clone().unwrap_or_else(|| {
|
let allowed_tools = def.allowed_tools.clone().unwrap_or_else(|| {
|
||||||
if def.role == "reviewer" {
|
if def.role == "reviewer" {
|
||||||
@@ -39,5 +40,6 @@ pub fn build_subagent_context(def: AgentDefinition) -> SubagentContext {
|
|||||||
allowed_tools,
|
allowed_tools,
|
||||||
max_steps,
|
max_steps,
|
||||||
session_dir: PathBuf::new(),
|
session_dir: PathBuf::new(),
|
||||||
|
workspaces: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ pub fn run_subagent(ctx: SubagentContext, tx: mpsc::Sender<SubagentEvent>) -> an
|
|||||||
|
|
||||||
let tool_ctx = crate::tool::ToolCtx::builder()
|
let tool_ctx = crate::tool::ToolCtx::builder()
|
||||||
.session_dir(ctx.session_dir.clone())
|
.session_dir(ctx.session_dir.clone())
|
||||||
|
.workspaces(ctx.workspaces.clone())
|
||||||
.origin(crate::app::state::types::Origin::SubAgent)
|
.origin(crate::app::state::types::Origin::SubAgent)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ fn spawn_single_agent(
|
|||||||
prompt: &str,
|
prompt: &str,
|
||||||
findings_snapshot: Vec<String>,
|
findings_snapshot: Vec<String>,
|
||||||
live: Option<&LiveStateFn>,
|
live: Option<&LiveStateFn>,
|
||||||
|
session_dir: &std::path::Path,
|
||||||
|
workspaces: &[std::path::PathBuf],
|
||||||
) -> anyhow::Result<String> {
|
) -> anyhow::Result<String> {
|
||||||
use crate::app::subagent::context::build_subagent_context;
|
use crate::app::subagent::context::build_subagent_context;
|
||||||
use crate::app::subagent::engine::run_subagent;
|
use crate::app::subagent::engine::run_subagent;
|
||||||
@@ -104,6 +106,8 @@ fn spawn_single_agent(
|
|||||||
let def = AgentDefinition::new(agent_name.to_string(), "coder".to_string())
|
let def = AgentDefinition::new(agent_name.to_string(), "coder".to_string())
|
||||||
.with_max_steps(50);
|
.with_max_steps(50);
|
||||||
let mut ctx = build_subagent_context(def);
|
let mut ctx = build_subagent_context(def);
|
||||||
|
ctx.session_dir = session_dir.to_path_buf();
|
||||||
|
ctx.workspaces = workspaces.to_vec();
|
||||||
|
|
||||||
let findings_section = if findings_snapshot.is_empty() {
|
let findings_section = if findings_snapshot.is_empty() {
|
||||||
String::new()
|
String::new()
|
||||||
@@ -182,6 +186,8 @@ pub fn execute_primitive(
|
|||||||
concurrency_cap: usize,
|
concurrency_cap: usize,
|
||||||
continue_on_error: bool,
|
continue_on_error: bool,
|
||||||
live: Option<&LiveStateFn>,
|
live: Option<&LiveStateFn>,
|
||||||
|
session_dir: &std::path::Path,
|
||||||
|
workspaces: &[std::path::PathBuf],
|
||||||
) -> anyhow::Result<Vec<String>> {
|
) -> anyhow::Result<Vec<String>> {
|
||||||
match primitive {
|
match primitive {
|
||||||
ScriptPrimitive::Agent(prompt) => {
|
ScriptPrimitive::Agent(prompt) => {
|
||||||
@@ -189,7 +195,7 @@ pub fn execute_primitive(
|
|||||||
let findings_snapshot = FINDINGS.lock().map(|f| f.clone()).unwrap_or_default();
|
let findings_snapshot = FINDINGS.lock().map(|f| f.clone()).unwrap_or_default();
|
||||||
let agent_id = uuid::Uuid::new_v4().to_string();
|
let agent_id = uuid::Uuid::new_v4().to_string();
|
||||||
let agent_name = resolved.chars().take(40).collect::<String>();
|
let agent_name = resolved.chars().take(40).collect::<String>();
|
||||||
match spawn_single_agent(&agent_id, &agent_name, &resolved, findings_snapshot, live) {
|
match spawn_single_agent(&agent_id, &agent_name, &resolved, findings_snapshot, live, session_dir, workspaces) {
|
||||||
Ok(text) => Ok(vec![text]),
|
Ok(text) => Ok(vec![text]),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if continue_on_error {
|
if continue_on_error {
|
||||||
@@ -219,12 +225,16 @@ pub fn execute_primitive(
|
|||||||
let results = Arc::clone(&results);
|
let results = Arc::clone(&results);
|
||||||
let cap = concurrency_cap;
|
let cap = concurrency_cap;
|
||||||
let live_clone = live.cloned();
|
let live_clone = live.cloned();
|
||||||
|
let session_dir = session_dir.to_path_buf();
|
||||||
|
let workspaces = workspaces.to_vec();
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let _permit = sem.acquire();
|
let _permit = sem.acquire();
|
||||||
let result = execute_primitive(
|
let result = execute_primitive(
|
||||||
&script, &args, cap, continue_on_error,
|
&script, &args, cap, continue_on_error,
|
||||||
live_clone.as_ref(),
|
live_clone.as_ref(),
|
||||||
|
&session_dir,
|
||||||
|
&workspaces,
|
||||||
);
|
);
|
||||||
if let Ok(mut locked) = results.lock() {
|
if let Ok(mut locked) = results.lock() {
|
||||||
locked.push((idx, result));
|
locked.push((idx, result));
|
||||||
@@ -258,7 +268,7 @@ pub fn execute_primitive(
|
|||||||
// the global FINDINGS mutex.
|
// the global FINDINGS mutex.
|
||||||
let mut all = Vec::new();
|
let mut all = Vec::new();
|
||||||
for (idx, script) in scripts.iter().enumerate() {
|
for (idx, script) in scripts.iter().enumerate() {
|
||||||
match execute_primitive(script, args, concurrency_cap, continue_on_error, live) {
|
match execute_primitive(script, args, concurrency_cap, continue_on_error, live, session_dir, workspaces) {
|
||||||
Ok(outputs) => all.extend(outputs),
|
Ok(outputs) => all.extend(outputs),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if continue_on_error {
|
if continue_on_error {
|
||||||
@@ -273,7 +283,7 @@ pub fn execute_primitive(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ScriptPrimitive::Phase { name: _name, script } => {
|
ScriptPrimitive::Phase { name: _name, script } => {
|
||||||
execute_primitive(script, args, concurrency_cap, continue_on_error, live)
|
execute_primitive(script, args, concurrency_cap, continue_on_error, live, session_dir, workspaces)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,8 +292,13 @@ pub fn execute_primitive(
|
|||||||
/// summary string. Uses no live-state callback.
|
/// summary string. Uses no live-state callback.
|
||||||
///
|
///
|
||||||
/// Return: a human-readable summary string.
|
/// Return: a human-readable summary string.
|
||||||
pub fn run_workflow(script: &WorkflowScript, args: &HashMap<String, String>) -> anyhow::Result<String> {
|
pub fn run_workflow(
|
||||||
run_workflow_tracked(script, args, None)
|
script: &WorkflowScript,
|
||||||
|
args: &HashMap<String, String>,
|
||||||
|
session_dir: &std::path::Path,
|
||||||
|
workspaces: &[std::path::PathBuf],
|
||||||
|
) -> anyhow::Result<String> {
|
||||||
|
run_workflow_tracked(script, args, None, session_dir, workspaces)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run a `WorkflowScript` with real-time live-state callbacks so the TUI
|
/// Run a `WorkflowScript` with real-time live-state callbacks so the TUI
|
||||||
@@ -297,6 +312,8 @@ pub fn run_workflow_tracked(
|
|||||||
script: &WorkflowScript,
|
script: &WorkflowScript,
|
||||||
args: &HashMap<String, String>,
|
args: &HashMap<String, String>,
|
||||||
live: Option<LiveStateFn>,
|
live: Option<LiveStateFn>,
|
||||||
|
session_dir: &std::path::Path,
|
||||||
|
workspaces: &[std::path::PathBuf],
|
||||||
) -> anyhow::Result<String> {
|
) -> anyhow::Result<String> {
|
||||||
if let Ok(mut findings) = FINDINGS.lock() {
|
if let Ok(mut findings) = FINDINGS.lock() {
|
||||||
findings.clear();
|
findings.clear();
|
||||||
@@ -312,6 +329,7 @@ pub fn run_workflow_tracked(
|
|||||||
let results = execute_primitive(
|
let results = execute_primitive(
|
||||||
&script.script, args, concurrency_cap,
|
&script.script, args, concurrency_cap,
|
||||||
script.options.continue_on_error, live_ref,
|
script.options.continue_on_error, live_ref,
|
||||||
|
session_dir, workspaces,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let summary = if results.is_empty() {
|
let summary = if results.is_empty() {
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ pub enum ClientRequest {
|
|||||||
Submit(String),
|
Submit(String),
|
||||||
Resize(u16, u16),
|
Resize(u16, u16),
|
||||||
Close,
|
Close,
|
||||||
|
ScrollUp,
|
||||||
|
ScrollDown,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flattened chat message sent from daemon to client for transcript display.
|
/// Flattened chat message sent from daemon to client for transcript display.
|
||||||
|
|||||||
+22
-5
@@ -11,6 +11,7 @@ use std::sync::Mutex;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use crossterm::execute;
|
use crossterm::execute;
|
||||||
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
|
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
|
||||||
|
use crossterm::event::{EnableMouseCapture, DisableMouseCapture};
|
||||||
use ratatui::backend::CrosstermBackend;
|
use ratatui::backend::CrosstermBackend;
|
||||||
use ratatui::Terminal;
|
use ratatui::Terminal;
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ fn run_single_process() -> Result<()> {
|
|||||||
|
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
let mut stdout = io::stdout();
|
let mut stdout = io::stdout();
|
||||||
execute!(stdout, EnterAlternateScreen)?;
|
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
|
||||||
let backend = CrosstermBackend::new(stdout);
|
let backend = CrosstermBackend::new(stdout);
|
||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
terminal.clear()?;
|
terminal.clear()?;
|
||||||
@@ -124,7 +125,7 @@ fn run_single_process() -> Result<()> {
|
|||||||
let run_result = run_loop(&mut state, &mut terminal);
|
let run_result = run_loop(&mut state, &mut terminal);
|
||||||
|
|
||||||
let mut restore_stdout = io::stdout();
|
let mut restore_stdout = io::stdout();
|
||||||
let _ = execute!(restore_stdout, LeaveAlternateScreen);
|
let _ = execute!(restore_stdout, LeaveAlternateScreen, DisableMouseCapture);
|
||||||
let _ = disable_raw_mode();
|
let _ = disable_raw_mode();
|
||||||
|
|
||||||
if let Err(e) = run_result {
|
if let Err(e) = run_result {
|
||||||
@@ -413,6 +414,14 @@ fn run_daemon() -> Result<()> {
|
|||||||
apply_action(&mut state, Action::Resize(w, h));
|
apply_action(&mut state, Action::Resize(w, h));
|
||||||
apply_action(&mut state, Action::Tick);
|
apply_action(&mut state, Action::Tick);
|
||||||
}
|
}
|
||||||
|
ClientRequest::ScrollUp => {
|
||||||
|
apply_action(&mut state, Action::ScrollUp);
|
||||||
|
apply_action(&mut state, Action::Tick);
|
||||||
|
}
|
||||||
|
ClientRequest::ScrollDown => {
|
||||||
|
apply_action(&mut state, Action::ScrollDown);
|
||||||
|
apply_action(&mut state, Action::Tick);
|
||||||
|
}
|
||||||
ClientRequest::Close => {
|
ClientRequest::Close => {
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
@@ -449,7 +458,7 @@ fn run_daemon() -> Result<()> {
|
|||||||
/// through the daemon, since the daemon has no notion of "this client
|
/// through the daemon, since the daemon has no notion of "this client
|
||||||
/// wants to leave" beyond the explicit `Close` request.
|
/// wants to leave" beyond the explicit `Close` request.
|
||||||
fn run_attach(session_id: &str) -> Result<()> {
|
fn run_attach(session_id: &str) -> Result<()> {
|
||||||
use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers};
|
use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers, MouseEventKind};
|
||||||
use ipc::protocol::ClientRequest;
|
use ipc::protocol::ClientRequest;
|
||||||
|
|
||||||
let store = model::store::Store::new();
|
let store = model::store::Store::new();
|
||||||
@@ -460,7 +469,7 @@ fn run_attach(session_id: &str) -> Result<()> {
|
|||||||
|
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
let mut stdout = io::stdout();
|
let mut stdout = io::stdout();
|
||||||
execute!(stdout, EnterAlternateScreen)?;
|
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
|
||||||
let backend = CrosstermBackend::new(stdout);
|
let backend = CrosstermBackend::new(stdout);
|
||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
terminal.clear()?;
|
terminal.clear()?;
|
||||||
@@ -512,6 +521,13 @@ fn run_attach(session_id: &str) -> Result<()> {
|
|||||||
Event::Resize(w, h) => {
|
Event::Resize(w, h) => {
|
||||||
client.send(&ClientRequest::Resize(w, h))?;
|
client.send(&ClientRequest::Resize(w, h))?;
|
||||||
}
|
}
|
||||||
|
Event::Mouse(mouse_event) => {
|
||||||
|
if mouse_event.kind == MouseEventKind::ScrollUp {
|
||||||
|
client.send(&ClientRequest::ScrollUp)?;
|
||||||
|
} else if mouse_event.kind == MouseEventKind::ScrollDown {
|
||||||
|
client.send(&ClientRequest::ScrollDown)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -544,7 +560,7 @@ fn run_attach(session_id: &str) -> Result<()> {
|
|||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = execute!(io::stdout(), LeaveAlternateScreen);
|
let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
|
||||||
let _ = disable_raw_mode();
|
let _ = disable_raw_mode();
|
||||||
|
|
||||||
let _ = client_state.settings.save();
|
let _ = client_state.settings.save();
|
||||||
@@ -569,6 +585,7 @@ fn run_loop(
|
|||||||
if let Err(ref _e) = result {
|
if let Err(ref _e) = result {
|
||||||
let _ = terminal.clear();
|
let _ = terminal.clear();
|
||||||
|
|
||||||
|
let _ = execute!(io::stdout(), DisableMouseCapture);
|
||||||
let _ = disable_raw_mode();
|
let _ = disable_raw_mode();
|
||||||
let _ = execute!(io::stdout(), LeaveAlternateScreen);
|
let _ = execute!(io::stdout(), LeaveAlternateScreen);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ impl Default for ToolCtxBuilder {
|
|||||||
impl ToolCtxBuilder {
|
impl ToolCtxBuilder {
|
||||||
/// Set the session directory.
|
/// Set the session directory.
|
||||||
pub fn session_dir(mut self, v: PathBuf) -> Self { self.session_dir = v; self }
|
pub fn session_dir(mut self, v: PathBuf) -> Self { self.session_dir = v; self }
|
||||||
|
/// Set the workspaces.
|
||||||
|
pub fn workspaces(mut self, v: Vec<PathBuf>) -> Self { self.workspaces = v; self }
|
||||||
/// Set the origin (main process vs. daemon-attached).
|
/// Set the origin (main process vs. daemon-attached).
|
||||||
pub fn origin(mut self, v: crate::app::state::types::Origin) -> Self { self.origin = v; self }
|
pub fn origin(mut self, v: crate::app::state::types::Origin) -> Self { self.origin = v; self }
|
||||||
/// Set the lsp_manager.
|
/// Set the lsp_manager.
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ impl Tool for SpawnAgents {
|
|||||||
max_concurrency,
|
max_concurrency,
|
||||||
true,
|
true,
|
||||||
live.as_ref(),
|
live.as_ref(),
|
||||||
|
&_ctx.session_dir,
|
||||||
|
&_ctx.workspaces,
|
||||||
)?;
|
)?;
|
||||||
format_results(results, "parallel")
|
format_results(results, "parallel")
|
||||||
}
|
}
|
||||||
@@ -193,6 +195,8 @@ impl Tool for SpawnPipeline {
|
|||||||
1,
|
1,
|
||||||
false,
|
false,
|
||||||
live.as_ref(),
|
live.as_ref(),
|
||||||
|
&_ctx.session_dir,
|
||||||
|
&_ctx.workspaces,
|
||||||
)?;
|
)?;
|
||||||
format_results(results, "pipeline")
|
format_results(results, "pipeline")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,9 @@ impl Tool for WorkflowRun {
|
|||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
crate::app::workflow::engine::run_workflow(&workflow_script, &workflow_args)
|
crate::app::workflow::engine::run_workflow(
|
||||||
|
&workflow_script, &workflow_args, &_ctx.session_dir, &_ctx.workspaces,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user