1mod agents;
54mod builtins;
55mod cypher;
56mod file_table;
57mod graph;
58mod imports;
59mod overlay;
60mod snapshot;
61mod state;
62mod symbol_graph;
63mod trigram;
64mod versions;
65mod walker;
66mod words;
67
68use std::path::Path;
69use std::sync::{Arc, Mutex};
70
71use harn_vm::VmValue;
72
73use crate::error::HostlibError;
74use crate::registry::{BuiltinRegistry, HostlibCapability, RegisteredBuiltin, SyncHandler};
75
76pub use agents::{AgentId, AgentInfo, AgentRegistry, AgentState, RegistryConfig};
77pub use builtins::SharedIndex;
78pub use cypher::{CypherError, CypherRow, CypherValue};
79pub use file_table::{FileId, IndexedFile, IndexedSymbol};
80pub use graph::DepGraph;
81pub use overlay::{BranchOverlay, OverlayState};
82pub use snapshot::{CodeIndexSnapshot, SnapshotMeta};
83pub use state::{BuildOutcome, IndexState};
84pub use symbol_graph::{Edge, EdgeKind, Node, NodeId, NodeKind, SymbolGraph};
85pub use trigram::TrigramIndex;
86pub use versions::{ChangeRecord, EditOp, VersionEntry, VersionLog, HISTORY_LIMIT};
87pub use words::{WordHit, WordIndex};
88
89#[derive(Clone, Default)]
97pub struct CodeIndexCapability {
98 index: SharedIndex,
99 current_agent: Arc<Mutex<Option<AgentId>>>,
100}
101
102impl CodeIndexCapability {
103 pub fn new() -> Self {
106 Self {
107 index: Arc::new(Mutex::new(None)),
108 current_agent: Arc::new(Mutex::new(None)),
109 }
110 }
111
112 pub fn shared(&self) -> SharedIndex {
116 self.index.clone()
117 }
118
119 pub fn current_agent_slot(&self) -> Arc<Mutex<Option<AgentId>>> {
123 self.current_agent.clone()
124 }
125
126 pub fn set_current_agent(&self, id: Option<AgentId>) -> Option<AgentId> {
129 let mut guard = self.current_agent.lock().expect("current_agent poisoned");
130 std::mem::replace(&mut *guard, id)
131 }
132
133 pub fn restore_from_disk(&self, workspace_root: &Path) -> std::io::Result<bool> {
143 match CodeIndexSnapshot::load(workspace_root)? {
144 Some(snap) => {
145 let mut state = IndexState::from_snapshot(snap);
146 state.reap_after_recovery(state::now_unix_ms());
147 let mut guard = self.index.lock().expect("code_index mutex poisoned");
148 *guard = Some(state);
149 Ok(true)
150 }
151 None => Ok(false),
152 }
153 }
154
155 pub fn persist_to_disk(&self) -> std::io::Result<bool> {
159 let snap = {
160 let guard = self.index.lock().expect("code_index mutex poisoned");
161 guard
162 .as_ref()
163 .map(|state| (state.snapshot(), state.root.clone()))
164 };
165 match snap {
166 Some((snap, root)) => {
167 snap.save(&root)?;
168 Ok(true)
169 }
170 None => Ok(false),
171 }
172 }
173}
174
175impl HostlibCapability for CodeIndexCapability {
176 fn module_name(&self) -> &'static str {
177 "code_index"
178 }
179
180 fn register_builtins(&self, registry: &mut BuiltinRegistry) {
181 register(
183 registry,
184 self.index.clone(),
185 builtins::BUILTIN_QUERY,
186 "query",
187 builtins::run_query,
188 );
189 register(
190 registry,
191 self.index.clone(),
192 builtins::BUILTIN_REBUILD,
193 "rebuild",
194 builtins::run_rebuild,
195 );
196 register(
197 registry,
198 self.index.clone(),
199 builtins::BUILTIN_STATS,
200 "stats",
201 builtins::run_stats,
202 );
203 register(
204 registry,
205 self.index.clone(),
206 builtins::BUILTIN_IMPORTS_FOR,
207 "imports_for",
208 builtins::run_imports_for,
209 );
210 register(
211 registry,
212 self.index.clone(),
213 builtins::BUILTIN_IMPORTERS_OF,
214 "importers_of",
215 builtins::run_importers_of,
216 );
217
218 register(
220 registry,
221 self.index.clone(),
222 builtins::BUILTIN_PATH_TO_ID,
223 "path_to_id",
224 builtins::run_path_to_id,
225 );
226 register(
227 registry,
228 self.index.clone(),
229 builtins::BUILTIN_ID_TO_PATH,
230 "id_to_path",
231 builtins::run_id_to_path,
232 );
233 register(
234 registry,
235 self.index.clone(),
236 builtins::BUILTIN_FILE_IDS,
237 "file_ids",
238 builtins::run_file_ids,
239 );
240 register(
241 registry,
242 self.index.clone(),
243 builtins::BUILTIN_FILE_META,
244 "file_meta",
245 builtins::run_file_meta,
246 );
247 register(
248 registry,
249 self.index.clone(),
250 builtins::BUILTIN_FILE_HASH,
251 "file_hash",
252 builtins::run_file_hash,
253 );
254
255 register(
257 registry,
258 self.index.clone(),
259 builtins::BUILTIN_READ_RANGE,
260 "read_range",
261 builtins::run_read_range,
262 );
263 register(
264 registry,
265 self.index.clone(),
266 builtins::BUILTIN_REINDEX_FILE,
267 "reindex_file",
268 builtins::run_reindex_file,
269 );
270 register(
271 registry,
272 self.index.clone(),
273 builtins::BUILTIN_TRIGRAM_QUERY,
274 "trigram_query",
275 builtins::run_trigram_query,
276 );
277 register(
278 registry,
279 self.index.clone(),
280 builtins::BUILTIN_EXTRACT_TRIGRAMS,
281 "extract_trigrams",
282 builtins::run_extract_trigrams,
283 );
284 register(
285 registry,
286 self.index.clone(),
287 builtins::BUILTIN_WORD_GET,
288 "word_get",
289 builtins::run_word_get,
290 );
291 register(
292 registry,
293 self.index.clone(),
294 builtins::BUILTIN_DEPS_GET,
295 "deps_get",
296 builtins::run_deps_get,
297 );
298 register(
299 registry,
300 self.index.clone(),
301 builtins::BUILTIN_OUTLINE_GET,
302 "outline_get",
303 builtins::run_outline_get,
304 );
305
306 register(
308 registry,
309 self.index.clone(),
310 builtins::BUILTIN_CURRENT_SEQ,
311 "current_seq",
312 builtins::run_current_seq,
313 );
314 register(
315 registry,
316 self.index.clone(),
317 builtins::BUILTIN_CHANGES_SINCE,
318 "changes_since",
319 builtins::run_changes_since,
320 );
321 register(
322 registry,
323 self.index.clone(),
324 builtins::BUILTIN_VERSION_RECORD,
325 "version_record",
326 builtins::run_version_record,
327 );
328
329 register(
331 registry,
332 self.index.clone(),
333 builtins::BUILTIN_AGENT_REGISTER,
334 "agent_register",
335 builtins::run_agent_register,
336 );
337 register(
338 registry,
339 self.index.clone(),
340 builtins::BUILTIN_AGENT_HEARTBEAT,
341 "agent_heartbeat",
342 builtins::run_agent_heartbeat,
343 );
344 register(
345 registry,
346 self.index.clone(),
347 builtins::BUILTIN_AGENT_UNREGISTER,
348 "agent_unregister",
349 builtins::run_agent_unregister,
350 );
351 register(
352 registry,
353 self.index.clone(),
354 builtins::BUILTIN_LOCK_TRY,
355 "lock_try",
356 builtins::run_lock_try,
357 );
358 register(
359 registry,
360 self.index.clone(),
361 builtins::BUILTIN_LOCK_RELEASE,
362 "lock_release",
363 builtins::run_lock_release,
364 );
365 register(
366 registry,
367 self.index.clone(),
368 builtins::BUILTIN_STATUS,
369 "status",
370 builtins::run_status,
371 );
372
373 let slot = self.current_agent.clone();
377 let handler: SyncHandler =
378 Arc::new(move |args| builtins::run_current_agent_id(&slot, args));
379 registry.register(RegisteredBuiltin {
380 name: builtins::BUILTIN_CURRENT_AGENT_ID,
381 module: "code_index",
382 method: "current_agent_id",
383 handler,
384 });
385
386 register(
388 registry,
389 self.index.clone(),
390 builtins::BUILTIN_CYPHER,
391 "cypher",
392 builtins::run_cypher,
393 );
394 register(
395 registry,
396 self.index.clone(),
397 builtins::BUILTIN_BRANCH_OVERLAY,
398 "branch_overlay",
399 builtins::run_branch_overlay,
400 );
401 register(
402 registry,
403 self.index.clone(),
404 builtins::BUILTIN_FRESHNESS,
405 "freshness",
406 builtins::run_freshness,
407 );
408 }
409}
410
411fn register(
412 registry: &mut BuiltinRegistry,
413 index: SharedIndex,
414 name: &'static str,
415 method: &'static str,
416 runner: fn(&SharedIndex, &[VmValue]) -> Result<VmValue, HostlibError>,
417) {
418 let captured = index;
419 let handler: SyncHandler = Arc::new(move |args| runner(&captured, args));
420 registry.register(RegisteredBuiltin {
421 name,
422 module: "code_index",
423 method,
424 handler,
425 });
426}