Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:35:04.981Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

project.rs

921 lines · 28.9 KB · rust
1use std::{
2    path::{Path, PathBuf},
3    sync::Arc,
4};
5
6use anyhow::Context;
7use collections::{BTreeMap, HashMap};
8use gpui::Rgba;
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11use settings_json::parse_json_with_comments;
12use settings_macros::{MergeFrom, with_fallible_options};
13use util::serde::default_true;
14
15use crate::{
16    AllLanguageSettingsContent, DelayMs, ExtendingVec, ParseStatus, ProjectTerminalSettingsContent,
17    RootUserSettings, SaturatingBool, fallible_options,
18};
19
20#[with_fallible_options]
21#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
22pub struct LspSettingsMap(pub HashMap<Arc<str>, LspSettings>);
23
24impl IntoIterator for LspSettingsMap {
25    type Item = (Arc<str>, LspSettings);
26    type IntoIter = std::collections::hash_map::IntoIter<Arc<str>, LspSettings>;
27
28    fn into_iter(self) -> Self::IntoIter {
29        self.0.into_iter()
30    }
31}
32
33impl RootUserSettings for ProjectSettingsContent {
34    fn parse_json(json: &str) -> (Option<Self>, ParseStatus) {
35        fallible_options::parse_json(json)
36    }
37    fn parse_json_with_comments(json: &str) -> anyhow::Result<Self> {
38        parse_json_with_comments(json)
39    }
40}
41
42#[with_fallible_options]
43#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
44pub struct ProjectSettingsContent {
45    #[serde(flatten)]
46    pub all_languages: AllLanguageSettingsContent,
47
48    #[serde(flatten)]
49    pub worktree: WorktreeSettingsContent,
50
51    /// Configuration for language servers.
52    ///
53    /// The following settings can be overridden for specific language servers:
54    /// - initialization_options
55    ///
56    /// To override settings for a language, add an entry for that language server's
57    /// name to the lsp value.
58    /// Default: null
59    #[serde(default)]
60    pub lsp: LspSettingsMap,
61
62    pub terminal: Option<ProjectTerminalSettingsContent>,
63
64    /// Configuration for Debugger-related features
65    #[serde(default)]
66    pub dap: HashMap<Arc<str>, DapSettingsContent>,
67
68    /// Settings for context servers used for AI-related features.
69    #[serde(default)]
70    pub context_servers: HashMap<Arc<str>, ContextServerSettingsContent>,
71
72    /// Default timeout in seconds for context server tool calls.
73    /// Can be overridden per-server in context_servers configuration.
74    ///
75    /// Default: 60
76    pub context_server_timeout: Option<u64>,
77
78    /// Configuration for how direnv configuration should be loaded
79    pub load_direnv: Option<DirenvSettings>,
80
81    /// The list of custom Git hosting providers.
82    pub git_hosting_providers: Option<ExtendingVec<GitHostingProviderConfig>>,
83
84    /// Whether to disable all AI features in Omega.
85    ///
86    /// Default: false
87    pub disable_ai: Option<SaturatingBool>,
88}
89
90/// When to scan content of linked directories.
91#[derive(
92    Copy,
93    Clone,
94    Default,
95    Debug,
96    Serialize,
97    Deserialize,
98    PartialEq,
99    Eq,
100    JsonSchema,
101    MergeFrom,
102    strum::VariantArray,
103    strum::VariantNames,
104)]
105#[serde(rename_all = "snake_case")]
106pub enum ScanSymlinksSetting {
107    /// Always scan symlinked directories
108    Always,
109    /// Only scan symlinked directories when they've been expanded in the workspace
110    #[default]
111    Expanded,
112}
113
114#[with_fallible_options]
115#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
116pub struct WorktreeSettingsContent {
117    /// Whether to prevent this project from being shared in public channels.
118    ///
119    /// Default: false
120    #[serde(default)]
121    pub prevent_sharing_in_public_channels: bool,
122
123    /// Completely ignore files matching globs from `file_scan_exclusions`. Overrides
124    /// `file_scan_inclusions`.
125    ///
126    /// Default: [
127    ///   "**/.git",
128    ///   "**/.svn",
129    ///   "**/.hg",
130    ///   "**/.jj",
131    ///   "**/CVS",
132    ///   "**/.DS_Store",
133    ///   "**/Thumbs.db",
134    ///   "**/.classpath",
135    ///   "**/.settings"
136    /// ]
137    pub file_scan_exclusions: Option<Vec<String>>,
138
139    /// Always include files that match these globs when scanning for files, even if they're
140    /// ignored by git. This setting is overridden by `file_scan_exclusions`.
141    /// Default: [
142    ///  ".env*",
143    ///  "docker-compose.*.yml",
144    /// ]
145    pub file_scan_inclusions: Option<Vec<String>>,
146
147    /// When to scan content of linked directories.
148    ///
149    /// Default: expanded
150    pub scan_symlinks: Option<ScanSymlinksSetting>,
151
152    /// Treat the files matching these globs as `.env` files.
153    /// Default: ["**/.env*", "**/*.pem", "**/*.key", "**/*.cert", "**/*.crt", "**/secrets.yml"]
154    pub private_files: Option<ExtendingVec<String>>,
155
156    /// Treat the files matching these globs as hidden files. You can hide hidden files in the project panel.
157    /// Default: ["**/.*"]
158    pub hidden_files: Option<Vec<String>>,
159
160    /// Treat the files matching these globs as read-only. These files can be opened and viewed,
161    /// but cannot be edited. This is useful for generated files, build outputs, or files from
162    /// external dependencies that should not be modified directly.
163    /// Default: []
164    pub read_only_files: Option<Vec<String>>,
165}
166
167#[with_fallible_options]
168#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, Hash)]
169#[serde(rename_all = "snake_case")]
170pub struct LspSettings {
171    pub binary: Option<BinarySettings>,
172    /// Options passed to the language server at startup.
173    ///
174    /// Ref: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize
175    ///
176    /// Consult the documentation for the specific language server to see which settings are supported.
177    pub initialization_options: Option<serde_json::Value>,
178    /// Language server settings.
179    ///
180    /// Ref: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_configuration
181    ///
182    /// Consult the documentation for the specific language server to see which settings are supported.
183    pub settings: Option<serde_json::Value>,
184    /// If the server supports sending tasks over LSP extensions,
185    /// this setting can be used to enable or disable them in Omega.
186    /// Default: true
187    #[serde(default = "default_true")]
188    pub enable_lsp_tasks: bool,
189    pub fetch: Option<FetchSettings>,
190}
191
192impl Default for LspSettings {
193    fn default() -> Self {
194        Self {
195            binary: None,
196            initialization_options: None,
197            settings: None,
198            enable_lsp_tasks: true,
199            fetch: None,
200        }
201    }
202}
203
204#[with_fallible_options]
205#[derive(
206    Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, Hash,
207)]
208pub struct BinarySettings {
209    pub path: Option<String>,
210    pub arguments: Option<Vec<String>>,
211    pub env: Option<BTreeMap<String, String>>,
212    pub ignore_system_version: Option<bool>,
213}
214
215#[with_fallible_options]
216#[derive(
217    Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, Hash,
218)]
219pub struct FetchSettings {
220    // Whether to consider pre-releases for fetching
221    pub pre_release: Option<bool>,
222}
223
224/// Common language server settings.
225#[with_fallible_options]
226#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
227pub struct GlobalLspSettingsContent {
228    /// Whether to show the LSP servers button in the status bar.
229    ///
230    /// Default: `true`
231    pub button: Option<bool>,
232    /// The maximum amount of time to wait for responses from language servers, in seconds.
233    /// A value of `0` will result in no timeout being applied (causing all LSP responses to wait indefinitely until completed).
234    ///
235    /// Default: `120`
236    pub request_timeout: Option<u64>,
237    /// The maximum line length a buffer may contain before language server features are disabled for the entire buffer.
238    ///
239    /// Default: `20000`
240    #[schemars(range(min = 1))]
241    pub max_buffer_line_length: Option<u32>,
242    /// Settings for language server notifications
243    pub notifications: Option<LspNotificationSettingsContent>,
244    /// Rules for rendering LSP semantic tokens.
245    pub semantic_token_rules: Option<SemanticTokenRules>,
246}
247
248#[with_fallible_options]
249#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
250pub struct LspNotificationSettingsContent {
251    /// Timeout in milliseconds for automatically dismissing language server notifications.
252    /// Set to 0 to disable auto-dismiss.
253    ///
254    /// Default: 5000
255    pub dismiss_timeout_ms: Option<u64>,
256}
257
258/// Custom rules for rendering LSP semantic tokens.
259#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, JsonSchema)]
260#[serde(transparent)]
261pub struct SemanticTokenRules {
262    pub rules: Vec<SemanticTokenRule>,
263}
264
265impl SemanticTokenRules {
266    pub const FILE_NAME: &'static str = "semantic_token_rules.json";
267
268    pub fn load(file_path: &Path) -> anyhow::Result<Self> {
269        let rules_content = std::fs::read(file_path).with_context(|| {
270            anyhow::anyhow!(
271                "Could not read semantic token rules from {}",
272                file_path.display()
273            )
274        })?;
275
276        serde_json_lenient::from_slice::<SemanticTokenRules>(&rules_content).with_context(|| {
277            anyhow::anyhow!(
278                "Failed to parse semantic token rules from {}",
279                file_path.display()
280            )
281        })
282    }
283}
284
285impl crate::merge_from::MergeFrom for SemanticTokenRules {
286    fn merge_from(&mut self, other: &Self) {
287        self.rules.splice(0..0, other.rules.iter().cloned());
288    }
289}
290
291#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, JsonSchema)]
292#[serde(rename_all = "snake_case")]
293pub struct SemanticTokenRule {
294    pub token_type: Option<String>,
295    #[serde(default)]
296    pub token_modifiers: Vec<String>,
297    #[serde(default)]
298    pub style: Vec<String>,
299    pub foreground_color: Option<Rgba>,
300    pub background_color: Option<Rgba>,
301    pub underline: Option<SemanticTokenColorOverride>,
302    pub strikethrough: Option<SemanticTokenColorOverride>,
303    pub font_weight: Option<SemanticTokenFontWeight>,
304    pub font_style: Option<SemanticTokenFontStyle>,
305}
306
307impl SemanticTokenRule {
308    pub fn no_style_defined(&self) -> bool {
309        self.style.is_empty()
310            && self.foreground_color.is_none()
311            && self.background_color.is_none()
312            && self.underline.is_none()
313            && self.strikethrough.is_none()
314            && self.font_weight.is_none()
315            && self.font_style.is_none()
316    }
317}
318
319#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
320#[serde(untagged)]
321pub enum SemanticTokenColorOverride {
322    InheritForeground(bool),
323    Replace(Rgba),
324}
325
326#[derive(
327    Copy,
328    Clone,
329    Debug,
330    Default,
331    Serialize,
332    Deserialize,
333    PartialEq,
334    Eq,
335    JsonSchema,
336    MergeFrom,
337    strum::VariantArray,
338    strum::VariantNames,
339)]
340#[serde(rename_all = "snake_case")]
341pub enum SemanticTokenFontWeight {
342    #[default]
343    Normal,
344    Bold,
345}
346
347#[derive(
348    Copy,
349    Clone,
350    Debug,
351    Default,
352    Serialize,
353    Deserialize,
354    PartialEq,
355    Eq,
356    JsonSchema,
357    MergeFrom,
358    strum::VariantArray,
359    strum::VariantNames,
360)]
361#[serde(rename_all = "snake_case")]
362pub enum SemanticTokenFontStyle {
363    #[default]
364    Normal,
365    Italic,
366}
367
368#[with_fallible_options]
369#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
370#[serde(rename_all = "snake_case")]
371pub struct DapSettingsContent {
372    pub binary: Option<String>,
373    pub args: Option<Vec<String>>,
374    pub env: Option<HashMap<String, String>>,
375}
376
377#[with_fallible_options]
378#[derive(
379    Default, Copy, Clone, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema, MergeFrom,
380)]
381pub struct SessionSettingsContent {
382    /// Whether or not to restore unsaved buffers on restart.
383    ///
384    /// If this is true, user won't be prompted whether to save/discard
385    /// dirty files when closing the application.
386    ///
387    /// Default: true
388    pub restore_unsaved_buffers: Option<bool>,
389    /// Whether or not to skip worktree trust checks.
390    /// When trusted, project settings are synchronized automatically,
391    /// language and MCP servers are downloaded and started automatically.
392    ///
393    /// Omega defaults this to true; upstream Zed defaults it to false.
394    /// See OMEGA-DELTA-0001.
395    ///
396    /// Default: true
397    pub trust_all_worktrees: Option<bool>,
398}
399
400#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, MergeFrom, Debug)]
401#[serde(untagged, rename_all = "snake_case")]
402pub enum ContextServerSettingsContent {
403    Stdio {
404        /// Whether the context server is enabled.
405        #[serde(default = "default_true")]
406        enabled: bool,
407        /// Whether to run the context server on the remote server when using remote development.
408        ///
409        /// If this is false, the context server will always run on the local machine.
410        ///
411        /// Default: false
412        #[serde(default)]
413        remote: bool,
414        #[serde(flatten)]
415        command: ContextServerCommand,
416    },
417    Http {
418        /// Whether the context server is enabled.
419        #[serde(default = "default_true")]
420        enabled: bool,
421        /// The URL of the remote context server.
422        url: String,
423        /// Optional headers to send.
424        #[serde(skip_serializing_if = "HashMap::is_empty", default)]
425        headers: HashMap<String, String>,
426        /// Timeout for tool calls in seconds. Defaults to global context_server_timeout if not specified.
427        timeout: Option<u64>,
428        /// Pre-registered OAuth client credentials for authorization servers that
429        /// require out-of-band client registration.
430        #[serde(default, skip_serializing_if = "Option::is_none")]
431        oauth: Option<OAuthClientSettings>,
432    },
433    Extension {
434        /// Whether the context server is enabled.
435        #[serde(default = "default_true")]
436        enabled: bool,
437        /// Whether to run the context server on the remote server when using remote development.
438        ///
439        /// If this is false, the context server will always run on the local machine.
440        ///
441        /// Default: false
442        #[serde(default)]
443        remote: bool,
444        /// The settings for this context server specified by the extension.
445        ///
446        /// Consult the documentation for the context server to see what settings
447        /// are supported.
448        settings: serde_json::Value,
449    },
450}
451
452impl ContextServerSettingsContent {
453    pub fn set_enabled(&mut self, enabled: bool) {
454        match self {
455            ContextServerSettingsContent::Stdio {
456                enabled: custom_enabled,
457                ..
458            } => {
459                *custom_enabled = enabled;
460            }
461            ContextServerSettingsContent::Extension {
462                enabled: ext_enabled,
463                ..
464            } => *ext_enabled = enabled,
465            ContextServerSettingsContent::Http {
466                enabled: remote_enabled,
467                ..
468            } => *remote_enabled = enabled,
469        }
470    }
471}
472
473/// Pre-registered OAuth client credentials for MCP servers that don't support
474/// Dynamic Client Registration.
475#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, MergeFrom, Debug)]
476pub struct OAuthClientSettings {
477    /// The OAuth client ID obtained from out-of-band registration with the
478    /// authorization server.
479    pub client_id: String,
480    /// The OAuth client secret, if this is a confidential client. For security,
481    /// prefer providing this interactively; we will prompt and store it in
482    /// the system keychain.
483    #[serde(default, skip_serializing_if = "Option::is_none")]
484    pub client_secret: Option<String>,
485}
486
487#[with_fallible_options]
488#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, MergeFrom)]
489pub struct ContextServerCommand {
490    #[serde(rename = "command")]
491    pub path: PathBuf,
492    #[serde(default)]
493    pub args: Vec<String>,
494    pub env: Option<HashMap<String, String>>,
495    /// Timeout for tool calls in seconds. Defaults to 60 if not specified.
496    pub timeout: Option<u64>,
497}
498
499impl std::fmt::Debug for ContextServerCommand {
500    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
501        let filtered_env = self.env.as_ref().map(|env| {
502            env.iter()
503                .map(|(k, v)| {
504                    (
505                        k,
506                        if util::redact::should_redact(k) {
507                            "[REDACTED]"
508                        } else {
509                            v
510                        },
511                    )
512                })
513                .collect::<Vec<_>>()
514        });
515
516        f.debug_struct("ContextServerCommand")
517            .field("path", &self.path)
518            .field("args", &self.args)
519            .field("env", &filtered_env)
520            .finish()
521    }
522}
523
524#[with_fallible_options]
525#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
526pub struct GitSettings {
527    /// Whether or not to enable git integration.
528    ///
529    /// Default: true
530    #[serde(flatten)]
531    pub enabled: Option<GitEnabledSettings>,
532    /// Whether or not to show the git gutter.
533    ///
534    /// Default: tracked_files
535    pub git_gutter: Option<GitGutterSetting>,
536    /// Sets the debounce threshold (in milliseconds) after which changes are reflected in the git gutter.
537    ///
538    /// Default: 0
539    pub gutter_debounce: Option<u64>,
540    /// Whether or not to show git blame data inline in
541    /// the currently focused line.
542    ///
543    /// Default: on
544    pub inline_blame: Option<InlineBlameSettings>,
545    /// Git blame settings.
546    pub blame: Option<BlameSettings>,
547    /// Which information to show in the branch picker.
548    ///
549    /// Default: on
550    pub branch_picker: Option<BranchPickerSettingsContent>,
551    /// File diff settings.
552    pub file_diff: Option<FileDiffSettingsContent>,
553    /// How hunks are displayed visually in the editor.
554    ///
555    /// Default: staged_hollow
556    pub hunk_style: Option<GitHunkStyleSetting>,
557    /// How file paths are displayed in the git gutter.
558    ///
559    /// Default: file_name_first
560    pub path_style: Option<GitPathStyle>,
561    /// Whether to show the stage and restore buttons on diff hunks.
562    ///
563    /// Default: true
564    pub show_stage_restore_buttons: Option<bool>,
565    /// Directory where git worktrees are created, relative to the repository
566    /// working directory.
567    ///
568    /// When the resolved directory is outside the project root, the
569    /// project's directory name is automatically appended so that
570    /// sibling repos don't collide. For example, with the default
571    /// `"../worktrees"` and a project at `~/code/omega`, worktrees are
572    /// created under `~/code/worktrees/omega/`.
573    ///
574    /// When the resolved directory is inside the project root, no
575    /// extra component is added (it's already project-scoped).
576    ///
577    /// Examples:
578    /// - `"../worktrees"` — `~/code/worktrees/<project>/` (default)
579    /// - `".git/zed-worktrees"` — `<project>/.git/zed-worktrees/`
580    /// - `"my-worktrees"` — `<project>/my-worktrees/`
581    ///
582    /// Trailing slashes are ignored.
583    ///
584    /// Default: ../worktrees
585    pub worktree_directory: Option<String>,
586}
587
588#[with_fallible_options]
589#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
590#[serde(rename_all = "snake_case")]
591pub struct GitEnabledSettings {
592    pub disable_git: Option<bool>,
593    pub enable_status: Option<bool>,
594    pub enable_diff: Option<bool>,
595}
596
597impl GitEnabledSettings {
598    pub fn is_git_status_enabled(&self) -> bool {
599        !self.disable_git.unwrap_or(false) && self.enable_status.unwrap_or(true)
600    }
601
602    pub fn is_git_diff_enabled(&self) -> bool {
603        !self.disable_git.unwrap_or(false) && self.enable_diff.unwrap_or(true)
604    }
605}
606
607#[derive(
608    Clone,
609    Copy,
610    Debug,
611    PartialEq,
612    Default,
613    Serialize,
614    Deserialize,
615    JsonSchema,
616    MergeFrom,
617    strum::VariantArray,
618    strum::VariantNames,
619)]
620#[serde(rename_all = "snake_case")]
621pub enum GitGutterSetting {
622    /// Show git gutter in tracked files.
623    #[default]
624    TrackedFiles,
625    /// Hide git gutter
626    Hide,
627}
628
629#[derive(
630    Clone,
631    Copy,
632    Debug,
633    PartialEq,
634    Default,
635    Serialize,
636    Deserialize,
637    JsonSchema,
638    MergeFrom,
639    strum::VariantArray,
640    strum::VariantNames,
641)]
642#[serde(rename_all = "snake_case")]
643pub enum InlineBlameLocation {
644    /// Show git blame inline at the current line.
645    #[default]
646    Inline,
647    /// Show git blame in the status bar at the bottom of the window.
648    StatusBar,
649}
650
651#[with_fallible_options]
652#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
653#[serde(rename_all = "snake_case")]
654pub struct InlineBlameSettings {
655    /// Whether or not to show git blame data inline in
656    /// the currently focused line.
657    ///
658    /// Default: true
659    pub enabled: Option<bool>,
660    /// Whether to only show the inline blame information
661    /// after a delay once the cursor stops moving.
662    ///
663    /// Default: 0
664    pub delay_ms: Option<DelayMs>,
665    /// Where to render the blame information when enabled.
666    ///
667    /// Default: inline
668    pub location: Option<InlineBlameLocation>,
669    /// The amount of padding between the end of the source line and the start
670    /// of the inline blame in units of columns.
671    ///
672    /// Default: 7
673    pub padding: Option<u32>,
674    /// The minimum column number to show the inline blame information at
675    ///
676    /// Default: 0
677    pub min_column: Option<u32>,
678    /// Whether to show commit summary as part of the inline blame.
679    ///
680    /// Default: false
681    pub show_commit_summary: Option<bool>,
682}
683
684#[with_fallible_options]
685#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
686#[serde(rename_all = "snake_case")]
687pub struct BlameSettings {
688    /// Whether to show the avatar of the author of the commit.
689    ///
690    /// Default: true
691    pub show_avatar: Option<bool>,
692}
693
694#[with_fallible_options]
695#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
696#[serde(rename_all = "snake_case")]
697pub struct BranchPickerSettingsContent {
698    /// Whether to show author name as part of the commit information.
699    ///
700    /// Default: false
701    pub show_author_name: Option<bool>,
702}
703
704#[with_fallible_options]
705#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
706#[serde(rename_all = "snake_case")]
707pub struct FileDiffSettingsContent {
708    /// Whether newly opened file diffs show the full file instead of changes only.
709    ///
710    /// Default: true
711    pub show_full_file: Option<bool>,
712}
713
714#[derive(
715    Clone,
716    Copy,
717    PartialEq,
718    Debug,
719    Default,
720    Serialize,
721    Deserialize,
722    JsonSchema,
723    MergeFrom,
724    strum::VariantArray,
725    strum::VariantNames,
726)]
727#[serde(rename_all = "snake_case")]
728pub enum GitHunkStyleSetting {
729    /// Show unstaged hunks with a filled background and staged hunks hollow.
730    #[default]
731    StagedHollow,
732    /// Show unstaged hunks hollow and staged hunks with a filled background.
733    UnstagedHollow,
734}
735
736#[with_fallible_options]
737#[derive(
738    Copy,
739    Clone,
740    Debug,
741    PartialEq,
742    Default,
743    Serialize,
744    Deserialize,
745    JsonSchema,
746    MergeFrom,
747    strum::VariantArray,
748    strum::VariantNames,
749)]
750#[serde(rename_all = "snake_case")]
751pub enum GitPathStyle {
752    /// Show file name first, then path
753    #[default]
754    FileNameFirst,
755    /// Show full path first
756    FilePathFirst,
757}
758
759#[with_fallible_options]
760#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
761pub struct DiagnosticsSettingsContent {
762    /// Whether to show the project diagnostics button in the status bar.
763    pub button: Option<bool>,
764
765    /// Whether or not to include warning diagnostics.
766    ///
767    /// Default: true
768    pub include_warnings: Option<bool>,
769
770    /// Settings for using LSP pull diagnostics mechanism in Omega.
771    pub lsp_pull_diagnostics: Option<LspPullDiagnosticsSettingsContent>,
772
773    /// Settings for showing inline diagnostics.
774    pub inline: Option<InlineDiagnosticsSettingsContent>,
775}
776
777#[with_fallible_options]
778#[derive(
779    Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq,
780)]
781pub struct LspPullDiagnosticsSettingsContent {
782    /// Whether to pull for diagnostics or not.
783    ///
784    /// Default: true
785    pub enabled: Option<bool>,
786    /// Minimum time to wait before pulling diagnostics from the language server(s).
787    /// 0 turns the debounce off.
788    ///
789    /// Default: 50
790    pub debounce_ms: Option<DelayMs>,
791}
792
793#[with_fallible_options]
794#[derive(
795    Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Eq,
796)]
797pub struct InlineDiagnosticsSettingsContent {
798    /// Whether or not to show inline diagnostics
799    ///
800    /// Default: false
801    pub enabled: Option<bool>,
802    /// Whether to only show the inline diagnostics after a delay after the
803    /// last editor event.
804    ///
805    /// Default: 150
806    pub update_debounce_ms: Option<DelayMs>,
807    /// The amount of padding between the end of the source line and the start
808    /// of the inline diagnostic in units of columns.
809    ///
810    /// Default: 4
811    pub padding: Option<u32>,
812    /// The minimum column to display inline diagnostics. This setting can be
813    /// used to horizontally align inline diagnostics at some position. Lines
814    /// longer than this value will still push diagnostics further to the right.
815    ///
816    /// Default: 0
817    pub min_column: Option<u32>,
818
819    pub max_severity: Option<DiagnosticSeverityContent>,
820}
821
822#[with_fallible_options]
823#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
824pub struct NodeBinarySettings {
825    /// The path to the Node binary.
826    pub path: Option<String>,
827    /// The path to the npm binary Omega should use (defaults to `.path/../npm`).
828    pub npm_path: Option<String>,
829    /// If enabled, Omega will download its own copy of Node.
830    pub ignore_system_version: Option<bool>,
831}
832
833#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
834#[serde(rename_all = "snake_case")]
835pub enum DirenvSettings {
836    /// Load direnv configuration through a shell hook
837    ShellHook,
838    /// Load direnv configuration directly using `direnv export json`
839    #[default]
840    Direct,
841    /// Do not load direnv configuration
842    Disabled,
843}
844
845#[derive(
846    Clone,
847    Copy,
848    Debug,
849    Eq,
850    PartialEq,
851    Ord,
852    PartialOrd,
853    Serialize,
854    Deserialize,
855    JsonSchema,
856    MergeFrom,
857    strum::VariantArray,
858    strum::VariantNames,
859)]
860#[serde(rename_all = "snake_case")]
861pub enum DiagnosticSeverityContent {
862    // No diagnostics are shown.
863    Off,
864    Error,
865    Warning,
866    Info,
867    Hint,
868    All,
869}
870
871/// A custom Git hosting provider.
872#[with_fallible_options]
873#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
874pub struct GitHostingProviderConfig {
875    /// The type of the provider.
876    ///
877    /// Must be one of `github`, `gitlab`, `bitbucket`, `gitea`, `forgejo`, or `source_hut`.
878    pub provider: GitHostingProviderKind,
879
880    /// The base URL for the provider (e.g., "https://code.corp.big.com").
881    pub base_url: String,
882
883    /// The display name for the provider (e.g., "BigCorp GitHub").
884    pub name: String,
885}
886
887#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
888#[serde(rename_all = "snake_case")]
889pub enum GitHostingProviderKind {
890    Github,
891    Gitlab,
892    Bitbucket,
893    Gitea,
894    Forgejo,
895    SourceHut,
896}
897
898#[cfg(test)]
899mod tests {
900    use super::*;
901
902    #[test]
903    fn test_stdio_context_server_without_args() {
904        let settings: ContextServerSettingsContent =
905            serde_json::from_str(r#"{ "command": "echo" }"#)
906                .expect("stdio context server without `args` should parse");
907        let ContextServerSettingsContent::Stdio { command, .. } = settings else {
908            panic!("expected Stdio variant, got {settings:?}");
909        };
910        assert_eq!(command.path, PathBuf::from("echo"));
911        assert!(command.args.is_empty());
912
913        let settings: ContextServerSettingsContent =
914            serde_json::from_str(r#"{ "command": "echo", "args": ["hello"] }"#).unwrap();
915        let ContextServerSettingsContent::Stdio { command, .. } = settings else {
916            panic!("expected Stdio variant, got {settings:?}");
917        };
918        assert_eq!(command.args, vec!["hello".to_string()]);
919    }
920}
921
Served at tenant.openagents/omega Member data and write actions are omitted.