algocline_core/engine_api.rs
1use async_trait::async_trait;
2
3// ─── Parameter types (transport-independent) ─────────────────────
4
5/// A single query response in a batch feed.
6#[derive(Debug)]
7pub struct QueryResponse {
8 /// Query ID (e.g. "q-0", "q-1").
9 pub query_id: String,
10 /// The host LLM's response for this query.
11 pub response: String,
12 /// Token usage reported by the host for this query.
13 pub usage: Option<crate::TokenUsage>,
14}
15
16// ─── Engine API trait ────────────────────────────────────────────
17
18/// Transport-independent API for the algocline engine.
19///
20/// Abstracts the full public surface of AppService so that callers
21/// (MCP handler, future daemon client, etc.) can operate through
22/// `Arc<dyn EngineApi>` without depending on the concrete implementation.
23///
24/// All methods are async to support both local (in-process) and remote
25/// (socket/HTTP) implementations uniformly.
26#[async_trait]
27pub trait EngineApi: Send + Sync {
28 // ─── Core execution ──────────────────────────────────────
29
30 /// Execute Lua code with optional JSON context.
31 async fn run(
32 &self,
33 code: Option<String>,
34 code_file: Option<String>,
35 ctx: Option<serde_json::Value>,
36 project_root: Option<String>,
37 ) -> Result<String, String>;
38
39 /// Apply an installed strategy package. Task is optional.
40 async fn advice(
41 &self,
42 strategy: &str,
43 task: Option<String>,
44 opts: Option<serde_json::Value>,
45 project_root: Option<String>,
46 ) -> Result<String, String>;
47
48 /// Continue a paused execution — single response (with optional query_id).
49 async fn continue_single(
50 &self,
51 session_id: &str,
52 response: String,
53 query_id: Option<&str>,
54 usage: Option<crate::TokenUsage>,
55 ) -> Result<String, String>;
56
57 /// Continue a paused execution — batch feed.
58 async fn continue_batch(
59 &self,
60 session_id: &str,
61 responses: Vec<QueryResponse>,
62 ) -> Result<String, String>;
63
64 // ─── Session status ──────────────────────────────────────
65
66 /// Query active session status.
67 async fn status(&self, session_id: Option<&str>) -> Result<String, String>;
68
69 // ─── Evaluation ──────────────────────────────────────────
70
71 /// Run an evalframe evaluation suite.
72 ///
73 /// `auto_card`: when true, emit an immutable Card
74 /// (`~/.algocline/cards/{strategy}/{card_id}.toml`) summarizing the run.
75 async fn eval(
76 &self,
77 scenario: Option<String>,
78 scenario_file: Option<String>,
79 scenario_name: Option<String>,
80 strategy: &str,
81 strategy_opts: Option<serde_json::Value>,
82 auto_card: bool,
83 ) -> Result<String, String>;
84
85 /// List eval history, optionally filtered by strategy.
86 async fn eval_history(&self, strategy: Option<&str>, limit: usize) -> Result<String, String>;
87
88 /// View a specific eval result by ID.
89 async fn eval_detail(&self, eval_id: &str) -> Result<String, String>;
90
91 /// Compare two eval results with statistical significance testing.
92 async fn eval_compare(&self, eval_id_a: &str, eval_id_b: &str) -> Result<String, String>;
93
94 // ─── Scenarios ───────────────────────────────────────────
95
96 /// List available scenarios.
97 async fn scenario_list(&self) -> Result<String, String>;
98
99 /// Show the content of a named scenario.
100 async fn scenario_show(&self, name: &str) -> Result<String, String>;
101
102 /// Install scenarios from a Git URL or local path.
103 async fn scenario_install(&self, url: String) -> Result<String, String>;
104
105 // ─── Packages ────────────────────────────────────────────
106
107 /// Link a local directory as a project-local package (symlink to cache).
108 ///
109 /// Phase 2 (subtask 5) will implement the full symlink logic.
110 async fn pkg_link(
111 &self,
112 path: String,
113 name: Option<String>,
114 force: Option<bool>,
115 ) -> Result<String, String>;
116
117 /// List installed packages with metadata.
118 ///
119 /// When `project_root` is provided, project-local packages from `alc.toml`/`alc.lock`
120 /// are included with `scope: "project"`. Global packages carry `scope: "global"`.
121 async fn pkg_list(&self, project_root: Option<String>) -> Result<String, String>;
122
123 /// Install a package from a Git URL or local path.
124 async fn pkg_install(&self, url: String, name: Option<String>) -> Result<String, String>;
125
126 /// Remove a symlinked package from `~/.algocline/packages/`.
127 ///
128 /// Only removes symlinks; for installed (copied) packages, use `pkg_remove`.
129 async fn pkg_unlink(&self, name: String) -> Result<String, String>;
130
131 /// Remove a package declaration from `alc.toml` and `alc.lock`.
132 ///
133 /// Requires an `alc.toml` to be found (via `project_root` or ancestor walk).
134 /// Does NOT delete physical files from `~/.algocline/packages/`.
135 async fn pkg_remove(
136 &self,
137 name: &str,
138 project_root: Option<String>,
139 version: Option<String>,
140 ) -> Result<String, String>;
141
142 // ─── Logging ─────────────────────────────────────────────
143
144 /// Append a note to a session's log file.
145 async fn add_note(
146 &self,
147 session_id: &str,
148 content: &str,
149 title: Option<&str>,
150 ) -> Result<String, String>;
151
152 /// View session logs.
153 async fn log_view(
154 &self,
155 session_id: Option<&str>,
156 limit: Option<usize>,
157 max_chars: Option<usize>,
158 ) -> Result<String, String>;
159
160 /// Aggregate stats across all logged sessions.
161 async fn stats(
162 &self,
163 strategy_filter: Option<&str>,
164 days: Option<u64>,
165 ) -> Result<String, String>;
166
167 // ─── Project lifecycle ────────────────────────────────────
168
169 /// Initialize `alc.toml` in the given project root.
170 ///
171 /// Creates a minimal `alc.toml` (`[packages]` section only).
172 /// Fails if `alc.toml` already exists (no overwrite).
173 async fn init(&self, project_root: Option<String>) -> Result<String, String>;
174
175 /// Re-resolve all `alc.toml` entries and rewrite `alc.lock`.
176 ///
177 /// Requires an `alc.toml` to be present. Returns resolved count and errors.
178 async fn update(&self, project_root: Option<String>) -> Result<String, String>;
179
180 /// Migrate a legacy `alc.lock` to `alc.toml` + new `alc.lock` format.
181 ///
182 /// Detects legacy format via `linked_at` / `local_dir` fields.
183 /// Backs up the old lock file as `alc.lock.bak`.
184 async fn migrate(&self, project_root: Option<String>) -> Result<String, String>;
185
186 // ─── Cards ───────────────────────────────────────────────
187
188 /// List Card summaries, optionally filtered by pkg.
189 async fn card_list(&self, pkg: Option<String>) -> Result<String, String>;
190
191 /// Fetch a full Card by id.
192 async fn card_get(&self, card_id: &str) -> Result<String, String>;
193
194 /// Filter/sort Cards using the Prisma-style `where` DSL.
195 ///
196 /// - `pkg`: restricts filesystem scan to a single pkg subdir (I/O hint).
197 /// - `where_`: nested-object predicate (see `card::parse_where`).
198 /// - `order_by`: array of dotted-path sort keys; `-` prefix = desc.
199 /// - `limit` / `offset`: pagination.
200 async fn card_find(
201 &self,
202 pkg: Option<String>,
203 where_: Option<serde_json::Value>,
204 order_by: Option<serde_json::Value>,
205 limit: Option<usize>,
206 offset: Option<usize>,
207 ) -> Result<String, String>;
208
209 /// List aliases, optionally filtered by pkg.
210 async fn card_alias_list(&self, pkg: Option<String>) -> Result<String, String>;
211
212 /// Resolve an alias name to its bound Card and return the full Card JSON.
213 async fn card_get_by_alias(&self, name: &str) -> Result<String, String>;
214
215 /// Bind (or rebind) an alias to a Card.
216 async fn card_alias_set(
217 &self,
218 name: &str,
219 card_id: &str,
220 pkg: Option<String>,
221 note: Option<String>,
222 ) -> Result<String, String>;
223
224 /// Append new top-level fields to an existing Card (additive-only).
225 async fn card_append(&self, card_id: &str, fields: serde_json::Value)
226 -> Result<String, String>;
227
228 /// Install Cards from a Card Collection repo (Git URL or local path).
229 async fn card_install(&self, url: String) -> Result<String, String>;
230
231 /// Read per-case samples from a Card's sidecar JSONL file.
232 ///
233 /// `where_` applies the same Prisma-style DSL used by `card_find`
234 /// to each sample row; offset/limit page the post-filter stream.
235 async fn card_samples(
236 &self,
237 card_id: &str,
238 offset: Option<usize>,
239 limit: Option<usize>,
240 where_: Option<serde_json::Value>,
241 ) -> Result<String, String>;
242
243 /// Walk a Card's lineage tree via `metadata.prior_card_id`.
244 ///
245 /// - `direction`: `"up"` | `"down"` | `"both"` (default `"up"`).
246 /// - `depth`: max traversal depth (default 10).
247 /// - `include_stats`: include each node's `[stats]` section.
248 /// - `relation_filter`: optional list of accepted `prior_relation` values.
249 async fn card_lineage(
250 &self,
251 card_id: &str,
252 direction: Option<String>,
253 depth: Option<usize>,
254 include_stats: Option<bool>,
255 relation_filter: Option<Vec<String>>,
256 ) -> Result<String, String>;
257
258 // ─── Hub ─────────────────────────────────────────────────
259
260 /// Rebuild hub index from a packages directory.
261 ///
262 /// When `source_dir` is provided, scans that directory directly
263 /// (pure metadata, no manifest). When omitted, scans `~/.algocline/packages/`.
264 async fn hub_reindex(
265 &self,
266 output_path: Option<String>,
267 source_dir: Option<String>,
268 ) -> Result<String, String>;
269
270 /// Show detailed information for a single package.
271 async fn hub_info(&self, pkg: String) -> Result<String, String>;
272
273 /// Search packages across remote index + local install state.
274 async fn hub_search(
275 &self,
276 query: Option<String>,
277 category: Option<String>,
278 installed_only: Option<bool>,
279 limit: Option<usize>,
280 ) -> Result<String, String>;
281
282 // ─── Diagnostics ─────────────────────────────────────────
283
284 /// Show server configuration and diagnostic info.
285 async fn info(&self) -> String;
286}