spec_ai_core/bootstrap_self/
plugin.rs

1use anyhow::Result;
2use serde_json::json;
3use std::path::PathBuf;
4
5use crate::persistence::Persistence;
6
7/// Context provided to each bootstrap plugin
8#[derive(Clone)]
9pub struct PluginContext<'a> {
10    pub persistence: &'a Persistence,
11    pub session_id: &'a str,
12    pub repo_root: &'a PathBuf,
13    pub mode: BootstrapMode,
14}
15
16/// Outcome from a single plugin's bootstrap run
17#[derive(Debug, Clone)]
18pub struct PluginOutcome {
19    pub plugin_name: String,
20    pub nodes_created: usize,
21    pub edges_created: usize,
22    pub root_node_id: Option<i64>, // The main entity node created by this plugin
23    pub phases: Vec<String>,
24    pub metadata: serde_json::Value,
25}
26
27impl PluginOutcome {
28    pub fn new(plugin_name: impl Into<String>) -> Self {
29        Self {
30            plugin_name: plugin_name.into(),
31            nodes_created: 0,
32            edges_created: 0,
33            root_node_id: None,
34            phases: Vec::new(),
35            metadata: json!({}),
36        }
37    }
38}
39
40#[derive(Clone, Copy)]
41pub enum BootstrapMode {
42    Fresh,
43    Refresh,
44}
45
46/// Trait that bootstrap plugins must implement
47pub trait BootstrapPlugin: Send + Sync {
48    /// The name of this plugin (e.g., "rust-cargo", "python-pyproject")
49    fn name(&self) -> &'static str;
50
51    /// Returns the bootstrap phases this plugin will execute
52    fn phases(&self) -> Vec<&'static str>;
53
54    /// Returns true if this plugin should be auto-activated for the given repository
55    fn should_activate(&self, repo_root: &PathBuf) -> bool;
56
57    /// Execute the bootstrap for this plugin
58    fn run(&self, context: PluginContext) -> Result<PluginOutcome>;
59}