Skip to main content

kaish_kernel/tools/
context.rs

1//! Execution context for tools.
2
3use std::path::PathBuf;
4use std::sync::Arc;
5
6use crate::ast::Value;
7use crate::backend::{KernelBackend, LocalBackend};
8use crate::interpreter::Scope;
9use crate::scheduler::JobManager;
10use crate::tools::ToolRegistry;
11use crate::vfs::VfsRouter;
12
13use super::traits::ToolSchema;
14
15/// Output context determines how command output should be formatted.
16///
17/// Different contexts prefer different output formats:
18/// - **Interactive** — Pretty columns, colors, traditional tree (TTY/REPL)
19/// - **Piped** — Raw output for pipeline processing
20/// - **Model** — Token-efficient compact formats (MCP server / agent context)
21/// - **Script** — Non-interactive script execution
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
23pub enum OutputContext {
24    /// Interactive TTY/REPL - use human-friendly format with colors.
25    #[default]
26    Interactive,
27    /// Output to another command - use raw output for pipes.
28    Piped,
29    /// MCP server / agent context - use token-efficient model format.
30    Model,
31    /// Non-interactive script - use raw output.
32    Script,
33}
34
35/// Execution context passed to tools.
36///
37/// Provides access to the backend (for file operations and tool dispatch),
38/// scope, and other kernel state.
39pub struct ExecContext {
40    /// Kernel backend for I/O operations.
41    ///
42    /// This is the preferred way to access filesystem operations.
43    /// Use `backend.read()`, `backend.write()`, etc.
44    pub backend: Arc<dyn KernelBackend>,
45    /// Variable scope.
46    pub scope: Scope,
47    /// Current working directory (VFS path).
48    pub cwd: PathBuf,
49    /// Previous working directory (for `cd -`).
50    pub prev_cwd: Option<PathBuf>,
51    /// Standard input for the tool (from pipeline).
52    pub stdin: Option<String>,
53    /// Structured data from pipeline (pre-parsed JSON from previous command).
54    /// Tools can check this before parsing stdin to avoid redundant JSON parsing.
55    pub stdin_data: Option<Value>,
56    /// Tool schemas for help command.
57    pub tool_schemas: Vec<ToolSchema>,
58    /// Tool registry reference (for tools that need to inspect available tools).
59    pub tools: Option<Arc<ToolRegistry>>,
60    /// Job manager for background jobs (optional).
61    pub job_manager: Option<Arc<JobManager>>,
62}
63
64impl ExecContext {
65    /// Create a new execution context with a VFS (uses LocalBackend without tools).
66    ///
67    /// This constructor is for backward compatibility and tests that don't need tool dispatch.
68    /// For full tool support, use `with_vfs_and_tools`.
69    pub fn new(vfs: Arc<VfsRouter>) -> Self {
70        Self {
71            backend: Arc::new(LocalBackend::new(vfs)),
72            scope: Scope::new(),
73            cwd: PathBuf::from("/"),
74            prev_cwd: None,
75            stdin: None,
76            stdin_data: None,
77            tool_schemas: Vec::new(),
78            tools: None,
79            job_manager: None,
80        }
81    }
82
83    /// Create a new execution context with VFS and tool registry.
84    ///
85    /// This is the preferred constructor for full kaish operation where
86    /// tools need to be dispatched through the backend.
87    pub fn with_vfs_and_tools(vfs: Arc<VfsRouter>, tools: Arc<ToolRegistry>) -> Self {
88        Self {
89            backend: Arc::new(LocalBackend::with_tools(vfs, tools.clone())),
90            scope: Scope::new(),
91            cwd: PathBuf::from("/"),
92            prev_cwd: None,
93            stdin: None,
94            stdin_data: None,
95            tool_schemas: Vec::new(),
96            tools: Some(tools),
97            job_manager: None,
98        }
99    }
100
101    /// Create a new execution context with a custom backend.
102    pub fn with_backend(backend: Arc<dyn KernelBackend>) -> Self {
103        Self {
104            backend,
105            scope: Scope::new(),
106            cwd: PathBuf::from("/"),
107            prev_cwd: None,
108            stdin: None,
109            stdin_data: None,
110            tool_schemas: Vec::new(),
111            tools: None,
112            job_manager: None,
113        }
114    }
115
116    /// Create a context with VFS, tools, and a specific scope.
117    pub fn with_vfs_tools_and_scope(vfs: Arc<VfsRouter>, tools: Arc<ToolRegistry>, scope: Scope) -> Self {
118        Self {
119            backend: Arc::new(LocalBackend::with_tools(vfs, tools.clone())),
120            scope,
121            cwd: PathBuf::from("/"),
122            prev_cwd: None,
123            stdin: None,
124            stdin_data: None,
125            tool_schemas: Vec::new(),
126            tools: Some(tools),
127            job_manager: None,
128        }
129    }
130
131    /// Create a context with a specific scope (uses LocalBackend without tools).
132    ///
133    /// For tests that don't need tool dispatch. For full tool support,
134    /// use `with_vfs_tools_and_scope`.
135    pub fn with_scope(vfs: Arc<VfsRouter>, scope: Scope) -> Self {
136        Self {
137            backend: Arc::new(LocalBackend::new(vfs)),
138            scope,
139            cwd: PathBuf::from("/"),
140            prev_cwd: None,
141            stdin: None,
142            stdin_data: None,
143            tool_schemas: Vec::new(),
144            tools: None,
145            job_manager: None,
146        }
147    }
148
149    /// Create a context with a custom backend and scope.
150    pub fn with_backend_and_scope(backend: Arc<dyn KernelBackend>, scope: Scope) -> Self {
151        Self {
152            backend,
153            scope,
154            cwd: PathBuf::from("/"),
155            prev_cwd: None,
156            stdin: None,
157            stdin_data: None,
158            tool_schemas: Vec::new(),
159            tools: None,
160            job_manager: None,
161        }
162    }
163
164    /// Set the available tool schemas (for help command).
165    pub fn set_tool_schemas(&mut self, schemas: Vec<ToolSchema>) {
166        self.tool_schemas = schemas;
167    }
168
169    /// Set the tool registry reference.
170    pub fn set_tools(&mut self, tools: Arc<ToolRegistry>) {
171        self.tools = Some(tools);
172    }
173
174    /// Set the job manager for background job tracking.
175    pub fn set_job_manager(&mut self, manager: Arc<JobManager>) {
176        self.job_manager = Some(manager);
177    }
178
179    /// Set stdin for this execution.
180    pub fn set_stdin(&mut self, stdin: String) {
181        self.stdin = Some(stdin);
182    }
183
184    /// Get stdin, consuming it.
185    pub fn take_stdin(&mut self) -> Option<String> {
186        self.stdin.take()
187    }
188
189    /// Set both text stdin and structured data.
190    ///
191    /// Use this when passing output through a pipeline where the previous
192    /// command produced structured data (e.g., JSON from MCP tools).
193    pub fn set_stdin_with_data(&mut self, text: String, data: Option<Value>) {
194        self.stdin = Some(text);
195        self.stdin_data = data;
196    }
197
198    /// Take structured data if available, consuming it.
199    ///
200    /// Tools can use this to avoid re-parsing JSON that was already parsed
201    /// by a previous command in the pipeline.
202    pub fn take_stdin_data(&mut self) -> Option<Value> {
203        self.stdin_data.take()
204    }
205
206    /// Resolve a path relative to cwd.
207    pub fn resolve_path(&self, path: &str) -> PathBuf {
208        if path.starts_with('/') {
209            PathBuf::from(path)
210        } else {
211            self.cwd.join(path)
212        }
213    }
214
215    /// Change the current working directory.
216    ///
217    /// Saves the old directory for `cd -` support.
218    pub fn set_cwd(&mut self, path: PathBuf) {
219        self.prev_cwd = Some(self.cwd.clone());
220        self.cwd = path;
221    }
222
223    /// Get the previous working directory (for `cd -`).
224    pub fn get_prev_cwd(&self) -> Option<&PathBuf> {
225        self.prev_cwd.as_ref()
226    }
227}