use std::collections::HashMap;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ContextEngineRecord {
#[serde(default = "default_context_enabled")]
pub enabled: bool,
#[serde(default = "default_context_auto_retrieve")]
pub auto_retrieve: bool,
#[serde(default = "default_max_rule_results")]
pub max_rule_results: i32,
#[serde(default = "default_rule_token_budget")]
pub rule_token_budget: i32,
#[serde(default)]
pub allow_hosted_embeddings: bool,
#[serde(default)]
pub semantic_embedding: bool,
#[serde(default)]
pub embedding_provider_url: Option<String>,
#[serde(default)]
pub embedding_provider_key: Option<String>,
#[serde(default)]
pub embedding_model: Option<String>,
#[serde(default)]
pub embedding_dim: Option<usize>,
}
const fn default_context_enabled() -> bool {
true
}
const fn default_context_auto_retrieve() -> bool {
true
}
const fn default_max_rule_results() -> i32 {
4
}
const fn default_rule_token_budget() -> i32 {
1500
}
impl Default for ContextEngineRecord {
fn default() -> Self {
Self {
enabled: true,
auto_retrieve: true,
max_rule_results: 4,
rule_token_budget: 1500,
allow_hosted_embeddings: false,
semantic_embedding: false,
embedding_provider_url: None,
embedding_provider_key: None,
embedding_model: None,
embedding_dim: None,
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReviewEngineRecord {
#[serde(default)]
pub multi_perspective: bool,
#[serde(default = "default_past_verdict_recall")]
pub past_verdict_recall: bool,
#[serde(default = "default_true")]
pub self_check_enabled: bool,
#[serde(default = "default_true")]
pub review_summary_enabled: bool,
#[serde(default = "default_true")]
pub hunk_line_resolution: bool,
#[serde(default)]
pub rule_applicability_judge: bool,
}
const fn default_past_verdict_recall() -> bool {
true
}
const fn default_true() -> bool {
true
}
impl Default for ReviewEngineRecord {
fn default() -> Self {
Self {
multi_perspective: false,
past_verdict_recall: default_past_verdict_recall(),
self_check_enabled: default_true(),
review_summary_enabled: default_true(),
hunk_line_resolution: default_true(),
rule_applicability_judge: false,
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileIntent {
pub file: String,
pub intent: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ReviewSummary {
pub one_line_summary: String,
pub walkthrough_by_file: Vec<FileIntent>,
pub blocking_count: u32,
pub non_blocking_count: u32,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AppSettingsRecord {
#[serde(default)]
pub proxy_enabled: bool,
#[serde(default = "default_proxy_port")]
pub proxy_port: i32,
#[serde(default = "default_language")]
pub language: String,
#[serde(default = "default_theme")]
pub theme: String,
#[serde(default)]
pub sound_notifications: bool,
#[serde(default)]
pub default_shell: Option<String>,
#[serde(default = "default_workspace")]
pub default_workspace: String,
#[serde(default)]
pub shortcuts: HashMap<String, String>,
#[serde(default)]
pub context_engine: ContextEngineRecord,
#[serde(default)]
pub review_engine: ReviewEngineRecord,
#[serde(default = "default_true")]
pub hints_mcp: bool,
#[serde(default = "default_fix_default_mode", rename = "fixDefaultMode")]
pub fix_default_mode: String,
#[serde(default, rename = "syncAuto")]
pub sync_auto: bool,
#[serde(default, rename = "cloudAutoLogin")]
pub cloud_auto_login: bool,
}
const fn default_proxy_port() -> i32 {
4000
}
fn default_language() -> String {
"en".into()
}
fn default_theme() -> String {
"dark".into()
}
fn default_workspace() -> String {
"~/projects".into()
}
fn default_fix_default_mode() -> String {
"preview".into()
}
impl Default for AppSettingsRecord {
fn default() -> Self {
Self {
proxy_enabled: false,
proxy_port: default_proxy_port(),
language: default_language(),
theme: default_theme(),
sound_notifications: false,
default_shell: None,
default_workspace: default_workspace(),
shortcuts: HashMap::new(),
context_engine: ContextEngineRecord::default(),
review_engine: ReviewEngineRecord::default(),
hints_mcp: true,
fix_default_mode: default_fix_default_mode(),
sync_auto: false,
cloud_auto_login: false,
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct RuntimeReadyEvent {
pub runtime: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProjectRecord {
pub id: String,
pub name: String,
pub path: String,
pub git_branch: Option<String>,
pub active_sessions: i32,
pub total_sessions: Option<i32>,
pub created_at: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AddProjectInput {
pub path: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RemoveProjectInput {
pub id: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProviderRecord {
pub id: String,
pub name: String,
pub base_url: String,
pub api_key: Option<String>,
pub model_mapping: HashMap<String, String>,
pub is_active: bool,
pub created_at: String,
pub updated_at: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ProviderAddInput {
pub name: String,
pub base_url: String,
pub model_mapping: HashMap<String, String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ProviderUpdateInput {
pub id: String,
pub name: Option<String>,
pub base_url: Option<String>,
pub model_mapping: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ProviderRemoveInput {
pub id: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProviderSetActiveInput {
pub id: String,
pub is_active: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SkillRecord {
pub id: String,
pub name: String,
pub source: String,
pub directory: String,
pub version: String,
pub description: String,
pub r#type: String,
pub engines: Vec<String>,
pub tags: Vec<String>,
pub trigger: Option<String>,
pub check_prompt: Option<String>,
pub repo_owner: Option<String>,
pub repo_name: Option<String>,
pub repo_branch: Option<String>,
pub readme_url: Option<String>,
pub enabled_for_codex: bool,
pub enabled_for_claude: bool,
pub enabled_for_gemini: bool,
pub enabled_for_cursor: bool,
pub installed_at: String,
pub updated_at: String,
pub enforcement: Option<String>,
#[serde(default = "default_origin")]
pub origin: String,
}
fn default_origin() -> String {
"manual".to_owned()
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct InstallSkillInput {
pub owner: String,
pub repo: String,
pub branch: String,
pub directory: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct RemoveSkillInput {
pub id: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ToggleSkillEngineInput {
pub id: String,
pub engine: String,
pub enabled: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DiscoverSkillsInput {
pub owner: String,
pub repo: String,
pub branch: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DiscoveredSkillRecord {
pub name: String,
pub description: String,
pub r#type: String,
pub engines: Vec<String>,
pub tags: Vec<String>,
pub version: String,
pub directory: String,
pub repo_owner: String,
pub repo_name: String,
pub repo_branch: String,
pub installed: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CreateLocalSkillInput {
pub name: String,
pub engines: Option<Vec<String>>,
pub tags: Option<Vec<String>>,
pub description: Option<String>,
pub r#type: Option<String>,
pub trigger: Option<String>,
pub check_prompt: Option<String>,
pub content: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RememberRuleInput {
pub title: String,
pub body: String,
#[serde(default)]
pub file_patterns: Option<Vec<String>>,
pub bad_code: Option<String>,
pub good_code: Option<String>,
pub severity: Option<String>,
#[serde(default)]
pub origin: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SkillRepoRecord {
pub id: String,
pub owner: String,
pub name: String,
pub branch: String,
pub enabled: bool,
pub created_at: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SkillRepoAddInput {
pub owner: String,
pub name: String,
pub branch: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SkillRepoRemoveInput {
pub id: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateConfidenceInput {
pub skill_id: String,
pub signal: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AddExampleInput {
pub skill_id: String,
pub bad_code: String,
pub good_code: String,
pub description: Option<String>,
pub source: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListExamplesInput {
pub skill_id: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct RemoveExampleInput {
pub id: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RuleExampleRecord {
pub id: String,
pub skill_id: String,
pub bad_code: String,
pub good_code: String,
pub description: Option<String>,
pub source: String,
pub created_at: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GitStatusInput {
pub project_path: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GitStatusRecord {
pub branch: Option<String>,
pub ahead: i32,
pub behind: i32,
pub files: Vec<GitFileStatusRecord>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GitFileStatusRecord {
pub path: String,
pub status: String,
pub additions: i32,
pub deletions: i32,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GitBranchesInput {
pub project_path: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GitBranchRecord {
pub name: String,
pub current: bool,
pub remote: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GitDiffInput {
pub project_path: String,
pub staged: Option<bool>,
pub ref1: Option<String>,
pub ref2: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DiffHunkRecord {
pub header: String,
pub body: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DiffContentRecord {
pub file_path: String,
pub hunks: Vec<DiffHunkRecord>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GitCommitInput {
pub project_path: String,
pub message: String,
pub files: Option<Vec<String>>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GitPushInput {
pub project_path: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GitCreatePRInput {
pub project_path: String,
pub title: String,
pub body: Option<String>,
pub base: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GitCheckoutPRInput {
pub project_path: String,
pub pr_number: Option<i32>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GitPRResult {
pub url: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EditorOpenInput {
pub project_path: String,
pub editor: Option<String>,
pub file_path: Option<String>,
pub line: Option<u32>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FilesSearchInput {
pub project_path: String,
pub query: String,
pub limit: Option<i32>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FilesReadInput {
pub project_path: String,
pub relative_path: String,
pub start_line: Option<i32>,
pub end_line: Option<i32>,
pub max_bytes: Option<i32>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileSearchResult {
pub path: String,
pub relative_path: String,
pub is_directory: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileReadRecord {
pub absolute_path: String,
pub relative_path: String,
pub content: String,
pub language: Option<String>,
pub line_count: i32,
pub truncated: bool,
pub sha256: Option<String>,
}
impl crate::domain::rule_view::RuleView for SkillRecord {
fn id(&self) -> &str {
&self.id
}
fn content(&self) -> &str {
&self.description
}
fn origin(&self) -> &str {
&self.origin
}
fn confidence(&self) -> Option<f64> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::domain::rule_view::RuleView;
#[test]
fn skill_record_implements_rule_view() {
let s = SkillRecord {
id: "id1".into(),
name: "n".into(),
source: "s".into(),
directory: "d".into(),
version: "0".into(),
description: "body".into(),
r#type: "review_standard".into(),
engines: vec![],
tags: vec![],
trigger: None,
check_prompt: None,
repo_owner: None,
repo_name: None,
repo_branch: None,
readme_url: None,
enabled_for_codex: false,
enabled_for_claude: false,
enabled_for_gemini: false,
enabled_for_cursor: false,
installed_at: String::new(),
updated_at: String::new(),
enforcement: None,
origin: "pr_review".into(),
};
assert_eq!(s.id(), "id1");
assert_eq!(s.content(), "body");
assert_eq!(s.origin(), "pr_review");
assert_eq!(s.confidence(), None);
}
#[test]
fn hunk_line_resolution_defaults_on() {
assert!(ReviewEngineRecord::default().hunk_line_resolution);
}
#[test]
fn hunk_line_resolution_defaults_on_for_pre_phase4_configs() {
let rec: ReviewEngineRecord = serde_json::from_str("{}").unwrap();
assert!(
rec.hunk_line_resolution,
"missing key must default to true (sharpens fix patches on upgrade)"
);
let off: ReviewEngineRecord =
serde_json::from_str(r#"{"hunkLineResolution": false}"#).unwrap();
assert!(!off.hunk_line_resolution);
}
#[test]
fn rule_applicability_judge_defaults_off() {
assert!(!ReviewEngineRecord::default().rule_applicability_judge);
let rec: ReviewEngineRecord = serde_json::from_str("{}").unwrap();
assert!(
!rec.rule_applicability_judge,
"missing key must default to false (no extra LLM call unless opted in)"
);
let on: ReviewEngineRecord =
serde_json::from_str(r#"{"ruleApplicabilityJudge": true}"#).unwrap();
assert!(on.rule_applicability_judge);
}
}