apollo/plugins/
manifest.rs1use std::path::Path;
4
5use anyhow::Context;
6use serde::Deserialize;
7
8use crate::config::Config;
9use crate::tools::toolsets::apply_package_manifest;
10
11#[derive(Debug, Clone, Deserialize, Default)]
12pub struct PluginManifestFile {
13 #[serde(default)]
14 pub version: u32,
15 #[serde(default)]
16 pub packages: Vec<String>,
17 #[serde(default)]
18 pub toolsets: ToolsetPatch,
19 #[serde(default)]
20 pub system_prompt_suffix: String,
21}
22
23#[derive(Debug, Clone, Deserialize, Default)]
24pub struct ToolsetPatch {
25 #[serde(default)]
26 pub enabled: Vec<String>,
27 #[serde(default)]
28 pub disabled: Vec<String>,
29}
30
31pub fn load_manifest(workspace: &Path, cfg: &Config) -> anyhow::Result<Option<PluginManifestFile>> {
32 if !cfg.plugin_layer.enabled {
33 return Ok(None);
34 }
35 let path = workspace
36 .join(".apollo")
37 .join(&cfg.plugin_layer.manifest_path);
38 if !path.is_file() {
39 return Ok(None);
40 }
41 let raw = std::fs::read_to_string(&path)
42 .with_context(|| format!("read plugin manifest {}", path.display()))?;
43 let m: PluginManifestFile = serde_json::from_str(&raw)
44 .with_context(|| format!("parse plugin manifest {}", path.display()))?;
45 Ok(Some(m))
46}
47
48pub fn merge_manifest_into_config(cfg: &mut Config, manifest: &PluginManifestFile) {
49 apply_package_manifest(&mut cfg.toolsets, &manifest.packages);
50 for e in &manifest.toolsets.enabled {
51 if !cfg.toolsets.enabled.contains(e) {
52 cfg.toolsets.enabled.push(e.clone());
53 }
54 }
55 for d in &manifest.toolsets.disabled {
56 if !cfg.toolsets.disabled.contains(d) {
57 cfg.toolsets.disabled.push(d.clone());
58 }
59 }
60 if !manifest.system_prompt_suffix.trim().is_empty() {
61 cfg.system_prompt.push('\n');
62 cfg.system_prompt
63 .push_str(manifest.system_prompt_suffix.trim());
64 }
65}
66
67pub fn apply_workspace_manifest(cfg: &mut Config, workspace: &Path) {
68 match load_manifest(workspace, cfg) {
69 Ok(Some(m)) => merge_manifest_into_config(cfg, &m),
70 Ok(None) => {}
71 Err(e) => tracing::warn!("plugin manifest skipped: {:#}", e),
72 }
73}