Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions .opencode/plugins/sce-agent-trace.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions .opencode/plugins/sce-bash-policy.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions .sce/config.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
{
"$schema": "https://sce.crocoder.dev/config.json",
"log_level": "error",
"integrations": {
"target": [
"claude",
"opencode"
]
},
"log_file": "context/tmp/sce.log",
"log_file_mode": "append",
"log_level": "error",
"policies": {
"attribution_hooks": {
"enabled": true
},
"bash": {
"presets": [
],
"custom": [
{
"id": "use-nix-flake-check-over-cargo-test",
Expand Down Expand Up @@ -42,7 +46,8 @@
},
"message": "This repository prefers `nix flake check` over direct `cargo check`. Run `nix flake check` instead."
}
]
],
"presets": []
}
}
}
4 changes: 4 additions & 0 deletions cli/src/services/config/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ where
bash_policy_presets: None,
bash_policy_custom: None,
database_retry: None,
integrations: None,
};
let mut validation_errors = Vec::new();
for loaded_path in &loaded_config_paths {
Expand Down Expand Up @@ -318,6 +319,9 @@ where
if let Some(database_retry) = layer.database_retry {
file_config.database_retry = Some(database_retry);
}
if let Some(integrations) = layer.integrations {
file_config.integrations = Some(integrations);
}
}

let mut resolved_log_level = ResolvedValue {
Expand Down
57 changes: 55 additions & 2 deletions cli/src/services/config/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ use serde::Deserialize;
use serde_json::Value;

use super::policy::{parse_bash_policy_presets, parse_custom_bash_policies, CustomBashPolicyEntry};
use super::types::{ConfigPathSource, DatabaseRetryConfig, LogFileMode, LogFormat, LogLevel};
use super::types::{
ConfigPathSource, DatabaseRetryConfig, IntegrationTargetId, IntegrationsConfig, LogFileMode,
LogFormat, LogLevel,
};
use crate::services::resilience::RetryPolicy;

pub(crate) const SCE_CONFIG_SCHEMA_JSON: &str =
Expand All @@ -34,10 +37,11 @@ pub(crate) const TOP_LEVEL_CONFIG_KEYS: &[&str] = &[
"timeout_ms",
super::resolver::WORKOS_CLIENT_ID_KEY.config_key,
"policies",
"integrations",
];

pub(crate) const TOP_LEVEL_CONFIG_KEYS_DESCRIPTION: &str =
"$schema, log_level, log_format, log_file, log_file_mode, timeout_ms, workos_client_id, policies";
"$schema, log_level, log_format, log_file, log_file_mode, timeout_ms, workos_client_id, policies, integrations";

static CONFIG_SCHEMA_VALIDATOR: OnceLock<Validator> = OnceLock::new();

Expand Down Expand Up @@ -68,6 +72,12 @@ pub(crate) struct ParsedFileConfigDocument {
pub(crate) timeout_ms: Option<u64>,
pub(crate) workos_client_id: Option<String>,
pub(crate) policies: Option<ParsedPoliciesConfigDocument>,
pub(crate) integrations: Option<ParsedIntegrationsConfigDocument>,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
pub(crate) struct ParsedIntegrationsConfigDocument {
pub(crate) target: Option<Vec<String>>,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
Expand Down Expand Up @@ -141,6 +151,7 @@ pub(crate) struct FileConfig {
pub(crate) bash_policy_presets: Option<FileConfigValue<Vec<String>>>,
pub(crate) bash_policy_custom: Option<FileConfigValue<Vec<CustomBashPolicyEntry>>>,
pub(crate) database_retry: Option<FileConfigValue<DatabaseRetryConfig>>,
pub(crate) integrations: Option<FileConfigValue<IntegrationsConfig>>,
}

pub(crate) type ParsedBashPolicyConfig = (
Expand Down Expand Up @@ -285,6 +296,7 @@ pub(crate) fn parse_file_config(
.map(|value| FileConfigValue { value, source });
let (attribution_hooks_enabled, bash_policy_presets, bash_policy_custom, database_retry) =
map_policies_config(typed.policies.as_ref(), object, path, source)?;
let integrations = map_integrations_config(typed.integrations.as_ref(), object, path, source)?;

Ok(FileConfig {
log_level,
Expand All @@ -297,6 +309,7 @@ pub(crate) fn parse_file_config(
bash_policy_presets,
bash_policy_custom,
database_retry,
integrations,
})
}

Expand Down Expand Up @@ -569,3 +582,43 @@ pub(crate) fn map_database_retry_config(
source,
}))
}

fn map_integrations_config(
typed: Option<&ParsedIntegrationsConfigDocument>,
object: &serde_json::Map<String, Value>,
path: &Path,
source: ConfigPathSource,
) -> Result<Option<FileConfigValue<IntegrationsConfig>>> {
let Some(integrations_value) = object.get("integrations") else {
return Ok(None);
};

let integrations_object = integrations_value.as_object().with_context(|| {
format!(
"Config key 'integrations' in '{}' must be an object.",
path.display()
)
})?;

validate_object_keys(
integrations_object,
path,
Some("integrations"),
&["target"],
"target",
)?;

let Some(raw_targets) = typed.and_then(|config| config.target.as_ref()) else {
return Ok(None);
};

let targets: Vec<IntegrationTargetId> = raw_targets
.iter()
.map(|raw| IntegrationTargetId::parse(raw, &format!("config file '{}'", path.display())))
.collect::<Result<Vec<_>>>()?;

Ok(Some(FileConfigValue {
value: IntegrationsConfig { target: targets },
source,
}))
}
23 changes: 23 additions & 0 deletions cli/src/services/config/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,26 @@ pub(crate) struct PerDbRetryConfig {
pub(crate) connection_open: Option<RetryPolicy>,
pub(crate) query: Option<RetryPolicy>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum IntegrationTargetId {
Opencode,
Claude,
}

impl IntegrationTargetId {
pub(crate) fn parse(raw: &str, source: &str) -> anyhow::Result<Self> {
match raw {
"opencode" => Ok(Self::Opencode),
"claude" => Ok(Self::Claude),
_ => anyhow::bail!(
"Invalid integration target '{raw}' from {source}. Valid values: opencode, claude."
),
}
}
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct IntegrationsConfig {
pub(crate) target: Vec<IntegrationTargetId>,
}
8 changes: 3 additions & 5 deletions cli/src/services/default_paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,6 @@ pub fn local_db_path() -> anyhow::Result<PathBuf> {
/// The path is `<state_root>/sce/auth.db`, where `state_root` comes
/// from the shared default-path catalog (`XDG_STATE_HOME` or platform
/// equivalent).
#[allow(dead_code)]
pub fn auth_db_path() -> anyhow::Result<PathBuf> {
Ok(resolve_sce_default_locations()?
.roots()
Expand All @@ -284,7 +283,6 @@ pub fn auth_db_path() -> anyhow::Result<PathBuf> {
/// The path is `<state_root>/sce/agent-trace.db`, where `state_root` comes
/// from the shared default-path catalog (`XDG_STATE_HOME` or platform
/// equivalent).
#[allow(dead_code)]
pub fn agent_trace_db_path() -> anyhow::Result<PathBuf> {
Ok(resolve_sce_default_locations()?
.roots()
Expand All @@ -298,7 +296,6 @@ pub fn agent_trace_db_path() -> anyhow::Result<PathBuf> {
/// The path is `<state_root>/sce/agent-trace-{checkout_id}.db`, where
/// `state_root` comes from the shared default-path catalog (`XDG_STATE_HOME` or
/// platform equivalent).
#[allow(dead_code)]
pub fn agent_trace_db_path_for_checkout(checkout_id: &str) -> anyhow::Result<PathBuf> {
let checkout_id = checkout_id.trim();
if checkout_id.is_empty() {
Expand Down Expand Up @@ -378,11 +375,13 @@ pub(crate) mod opencode_asset {
pub const AGENTS_DIR: &str = "agents";
}

#[allow(dead_code)]
pub(crate) mod claude_asset {
pub const CLAUDE_DIR: &str = "claude";
pub const SETTINGS_FILE: &str = "settings.json";
pub const HOOKS_DIR: &str = "hooks";
pub const SKILLS_DIR: &str = "skills";
pub const AGENTS_DIR: &str = "agents";
pub const COMMANDS_DIR: &str = "commands";
}

#[allow(dead_code)]
Expand Down Expand Up @@ -410,7 +409,6 @@ pub(crate) mod schema {
pub const SCE_CONFIG_SCHEMA: &str = "sce-config.schema.json";
}

#[allow(dead_code)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct RepoPaths {
root: PathBuf,
Expand Down
Loading
Loading