feat: projects as backend sources with system prompt injection (#8739)

Signed-off-by: Douwe Osinga <douwe@squareup.com>
Co-authored-by: Douwe Osinga <douwe@squareup.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Douwe Osinga
2026-05-07 10:30:15 -04:00
committed by GitHub
parent bced4ea5ec
commit ff42171903
29 changed files with 1241 additions and 919 deletions
+30 -4
View File
@@ -817,6 +817,7 @@ pub enum SourceType {
Recipe,
Subrecipe,
Agent,
Project,
}
impl std::fmt::Display for SourceType {
@@ -827,6 +828,7 @@ impl std::fmt::Display for SourceType {
SourceType::Recipe => write!(f, "recipe"),
SourceType::Subrecipe => write!(f, "subrecipe"),
SourceType::Agent => write!(f, "agent"),
SourceType::Project => write!(f, "project"),
}
}
}
@@ -842,10 +844,11 @@ pub struct SourceEntry {
pub name: String,
pub description: String,
pub content: String,
/// Absolute path to the source on disk. A directory for skills, a file for
/// recipes and agents. Built-in skills use read-only synthetic
/// `builtin://skills/<name>` paths.
pub directory: String,
/// Stable on-disk path identifying this source. Pass it back to
/// update/delete/export to operate on this entry. Skills use the directory
/// containing `SKILL.md`; projects use the project file path; built-in
/// skills use `builtin://skills/<name>` synthetic paths.
pub path: String,
/// True when the source lives in the user's global sources directory; false
/// when it lives inside a specific project.
pub global: bool,
@@ -853,6 +856,10 @@ pub struct SourceEntry {
/// Only skills currently populate this; empty for other source types.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub supporting_files: Vec<String>,
/// Arbitrary key/value pairs for type-specific metadata (e.g. icon, color,
/// preferredProvider for projects). Stored in the frontmatter.
#[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")]
pub properties: std::collections::HashMap<String, serde_json::Value>,
}
impl SourceEntry {
@@ -881,6 +888,14 @@ pub struct CreateSourceRequest {
/// Absolute path to the project root. Required when `global` is false.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub project_dir: Option<String>,
/// Project source ID. When set with `global: false`, the backend resolves
/// the project's first working directory automatically. Takes precedence
/// over `project_dir`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub project_id: Option<String>,
/// Arbitrary key/value metadata.
#[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")]
pub properties: std::collections::HashMap<String, serde_json::Value>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
@@ -903,6 +918,10 @@ pub struct ListSourcesRequest {
pub source_type: Option<SourceType>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub project_dir: Option<String>,
/// When true, also scan the working directories of all known projects for
/// project-scoped sources (e.g. skills stored under `{workingDir}/.agents/skills/`).
#[serde(default)]
pub include_project_sources: bool,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
@@ -922,6 +941,13 @@ pub struct UpdateSourceRequest {
pub name: String,
pub description: String,
pub content: String,
/// When `Some`, replaces all stored properties on the source. When
/// `None` (or omitted), the source's existing properties are
/// preserved. Callers that don't model the full property bag (e.g.
/// the skills editor, which only edits name/description/content)
/// should omit this so per-skill metadata isn't silently erased.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub properties: Option<std::collections::HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]