Compare commits

..

2 Commits

14 changed files with 148 additions and 238 deletions
-87
View File
@@ -888,7 +888,6 @@ exec bash --norc --noprofile
"kitty" => launch_macos_open_app("kitty", &script_file, false),
"ghostty" => launch_macos_open_app("Ghostty", &script_file, true),
"wezterm" => launch_macos_open_app("WezTerm", &script_file, true),
"tabby" => launch_macos_tabby(&script_file),
_ => launch_macos_terminal_app(&script_file), // "terminal" or default
};
@@ -1006,39 +1005,6 @@ fn launch_macos_open_app(
Ok(())
}
/// macOS: Tabby
#[cfg(target_os = "macos")]
fn launch_macos_tabby(script_file: &std::path::Path) -> Result<(), String> {
use std::process::Command;
let output = Command::new("open")
.args(build_macos_tabby_args(script_file))
.output()
.map_err(|e| format!("启动 Tabby 失败: {e}"))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(format!(
"Tabby 启动失败 (exit code: {:?}): {}",
output.status.code(),
stderr
));
}
Ok(())
}
fn build_macos_tabby_args(script_file: &std::path::Path) -> Vec<String> {
vec![
"-na".to_string(),
"Tabby".to_string(),
"--args".to_string(),
"run".to_string(),
"bash".to_string(),
script_file.to_string_lossy().into_owned(),
]
}
/// Linux: 根据用户首选终端启动
#[cfg(target_os = "linux")]
fn launch_linux_terminal(config_file: &std::path::Path) -> Result<(), String> {
@@ -1057,8 +1023,6 @@ fn launch_linux_terminal(config_file: &std::path::Path) -> Result<(), String> {
("alacritty", vec!["-e"]),
("kitty", vec!["-e"]),
("ghostty", vec!["-e"]),
("tabby", vec!["run"]),
("tabby-terminal", vec!["run"]),
];
// Create temp script file
@@ -1184,7 +1148,6 @@ del \"%~f0\" >nul 2>&1
"PowerShell",
),
"wt" => run_windows_start_command(&["wt", "cmd", "/K", &bat_path], "Windows Terminal"),
"tabby" => run_windows_tabby_command(&bat_path),
_ => run_windows_start_command(&["cmd", "/K", &bat_path], "cmd"), // "cmd" or default
};
@@ -1201,56 +1164,6 @@ del \"%~f0\" >nul 2>&1
result
}
#[cfg(target_os = "windows")]
fn run_windows_tabby_command(bat_path: &str) -> Result<(), String> {
use std::process::Command;
let mut last_error = String::from("未找到可用的 Tabby 可执行文件");
for candidate in windows_tabby_executable_candidates() {
let result = Command::new(&candidate)
.args(["run", "cmd", "/K", bat_path])
.creation_flags(CREATE_NO_WINDOW)
.spawn();
match result {
Ok(_) => return Ok(()),
Err(e) => {
last_error = format!("执行 {} 失败: {}", candidate.display(), e);
}
}
}
Err(last_error)
}
#[cfg(target_os = "windows")]
fn windows_tabby_executable_candidates() -> Vec<std::path::PathBuf> {
let mut candidates = Vec::new();
for candidate in ["tabby", "tabby.exe", "Tabby", "Tabby.exe"] {
push_unique_path(&mut candidates, std::path::PathBuf::from(candidate));
}
if let Some(local_app_data) = std::env::var_os("LOCALAPPDATA") {
let tabby_dir = std::path::PathBuf::from(local_app_data)
.join("Programs")
.join("Tabby");
push_unique_path(&mut candidates, tabby_dir.join("Tabby.exe"));
push_unique_path(&mut candidates, tabby_dir.join("tabby.exe"));
}
for env_key in ["ProgramFiles", "ProgramFiles(x86)"] {
if let Some(base_dir) = std::env::var_os(env_key) {
let tabby_dir = std::path::PathBuf::from(base_dir).join("Tabby");
push_unique_path(&mut candidates, tabby_dir.join("Tabby.exe"));
push_unique_path(&mut candidates, tabby_dir.join("tabby.exe"));
}
}
candidates
}
/// Windows: Run a start command with common error handling
#[cfg(target_os = "windows")]
fn run_windows_start_command(args: &[&str], terminal_name: &str) -> Result<(), String> {
+47 -23
View File
@@ -6,6 +6,32 @@ use indexmap::IndexMap;
use serde_json::{json, Map, Value};
use std::path::PathBuf;
const STANDARD_OMO_PLUGIN_PREFIXES: [&str; 2] = ["oh-my-openagent", "oh-my-opencode"];
const SLIM_OMO_PLUGIN_PREFIXES: [&str; 1] = ["oh-my-opencode-slim"];
fn matches_plugin_prefix(plugin_name: &str, prefix: &str) -> bool {
plugin_name == prefix
|| plugin_name
.strip_prefix(prefix)
.map(|suffix| suffix.starts_with('@'))
.unwrap_or(false)
}
fn matches_any_plugin_prefix(plugin_name: &str, prefixes: &[&str]) -> bool {
prefixes
.iter()
.any(|prefix| matches_plugin_prefix(plugin_name, prefix))
}
fn canonicalize_plugin_name(plugin_name: &str) -> String {
if let Some(suffix) = plugin_name.strip_prefix("oh-my-opencode") {
if suffix.is_empty() || suffix.starts_with('@') {
return format!("oh-my-openagent{suffix}");
}
}
plugin_name.to_string()
}
pub fn get_opencode_dir() -> PathBuf {
if let Some(override_dir) = get_opencode_override_dir() {
return override_dir;
@@ -140,58 +166,56 @@ pub fn remove_mcp_server(id: &str) -> Result<(), AppError> {
pub fn add_plugin(plugin_name: &str) -> Result<(), AppError> {
let mut config = read_opencode_config()?;
let normalized_plugin_name = canonicalize_plugin_name(plugin_name);
let plugins = config.get_mut("plugin").and_then(|v| v.as_array_mut());
match plugins {
Some(arr) => {
// Mutual exclusion: standard OMO and OMO Slim cannot coexist as plugins
if plugin_name.starts_with("oh-my-opencode")
&& !plugin_name.starts_with("oh-my-opencode-slim")
{
// Adding standard OMO -> remove all Slim variants
arr.retain(|v| {
v.as_str()
.map(|s| !s.starts_with("oh-my-opencode-slim"))
.unwrap_or(true)
});
} else if plugin_name.starts_with("oh-my-opencode-slim") {
// Adding Slim -> remove all standard OMO variants (but keep slim)
if matches_any_plugin_prefix(&normalized_plugin_name, &STANDARD_OMO_PLUGIN_PREFIXES) {
arr.retain(|v| {
v.as_str()
.map(|s| {
!s.starts_with("oh-my-opencode") || s.starts_with("oh-my-opencode-slim")
!matches_any_plugin_prefix(s, &STANDARD_OMO_PLUGIN_PREFIXES)
&& !matches_any_plugin_prefix(s, &SLIM_OMO_PLUGIN_PREFIXES)
})
.unwrap_or(true)
});
} else if matches_any_plugin_prefix(&normalized_plugin_name, &SLIM_OMO_PLUGIN_PREFIXES)
{
arr.retain(|v| {
v.as_str()
.map(|s| {
!matches_any_plugin_prefix(s, &STANDARD_OMO_PLUGIN_PREFIXES)
&& !matches_any_plugin_prefix(s, &SLIM_OMO_PLUGIN_PREFIXES)
})
.unwrap_or(true)
});
}
let already_exists = arr.iter().any(|v| v.as_str() == Some(plugin_name));
let already_exists = arr
.iter()
.any(|v| v.as_str() == Some(normalized_plugin_name.as_str()));
if !already_exists {
arr.push(Value::String(plugin_name.to_string()));
arr.push(Value::String(normalized_plugin_name));
}
}
None => {
config["plugin"] = json!([plugin_name]);
config["plugin"] = json!([normalized_plugin_name]);
}
}
write_opencode_config(&config)
}
pub fn remove_plugin_by_prefix(prefix: &str) -> Result<(), AppError> {
pub fn remove_plugins_by_prefixes(prefixes: &[&str]) -> Result<(), AppError> {
let mut config = read_opencode_config()?;
if let Some(arr) = config.get_mut("plugin").and_then(|v| v.as_array_mut()) {
arr.retain(|v| {
v.as_str()
.map(|s| {
if !s.starts_with(prefix) {
return true; // Keep: doesn't match prefix at all
}
let rest = &s[prefix.len()..];
rest.starts_with('-')
})
.map(|s| !matches_any_plugin_prefix(s, prefixes))
.unwrap_or(true)
});
@@ -974,7 +974,9 @@ mod tests {
"data: {\"type\":\"response.completed\",\"response\":{\"status\":\"completed\",\"usage\":{\"input_tokens\":5,\"output_tokens\":2}}}\n\n"
);
let upstream = stream::iter(vec![Ok(Bytes::from(input.as_bytes().to_vec()))]);
let upstream = stream::iter(vec![Ok::<_, std::io::Error>(Bytes::from(
input.as_bytes().to_vec(),
))]);
let converted = create_anthropic_sse_stream_from_responses(upstream);
let chunks: Vec<_> = converted.collect().await;
let events: Vec<Value> = chunks
+77 -24
View File
@@ -21,33 +21,41 @@ type OmoProfileData = (Option<Value>, Option<Value>, Option<Value>);
// ── Variant descriptor ─────────────────────────────────────────
pub struct OmoVariant {
pub filename: &'static str,
pub preferred_filename: &'static str,
pub config_candidates: &'static [&'static str],
pub category: &'static str,
pub provider_prefix: &'static str,
pub plugin_name: &'static str,
pub plugin_prefix: &'static str,
pub plugin_prefixes: &'static [&'static str],
pub has_categories: bool,
pub label: &'static str,
pub import_label: &'static str,
}
pub const STANDARD: OmoVariant = OmoVariant {
filename: "oh-my-opencode.jsonc",
preferred_filename: "oh-my-openagent.jsonc",
config_candidates: &[
"oh-my-openagent.jsonc",
"oh-my-openagent.json",
"oh-my-opencode.jsonc",
"oh-my-opencode.json",
],
category: "omo",
provider_prefix: "omo-",
plugin_name: "oh-my-opencode@latest",
plugin_prefix: "oh-my-opencode",
plugin_name: "oh-my-openagent@latest",
plugin_prefixes: &["oh-my-openagent", "oh-my-opencode"],
has_categories: true,
label: "OMO",
import_label: "Imported",
};
pub const SLIM: OmoVariant = OmoVariant {
filename: "oh-my-opencode-slim.jsonc",
preferred_filename: "oh-my-opencode-slim.jsonc",
config_candidates: &["oh-my-opencode-slim.jsonc", "oh-my-opencode-slim.json"],
category: "omo-slim",
provider_prefix: "omo-slim-",
plugin_name: "oh-my-opencode-slim@latest",
plugin_prefix: "oh-my-opencode-slim",
plugin_prefixes: &["oh-my-opencode-slim"],
has_categories: false,
label: "OMO Slim",
import_label: "Imported Slim",
@@ -60,22 +68,27 @@ pub struct OmoService;
impl OmoService {
// ── Path helpers ────────────────────────────────────────
fn config_candidates(v: &OmoVariant, base_dir: &Path) -> Vec<PathBuf> {
v.config_candidates
.iter()
.map(|name| base_dir.join(name))
.collect()
}
fn find_existing_config_path(v: &OmoVariant, base_dir: &Path) -> Option<PathBuf> {
Self::config_candidates(v, base_dir)
.into_iter()
.find(|path| path.exists())
}
fn config_path(v: &OmoVariant) -> PathBuf {
get_opencode_dir().join(v.filename)
let base_dir = get_opencode_dir();
Self::find_existing_config_path(v, &base_dir)
.unwrap_or_else(|| base_dir.join(v.preferred_filename))
}
fn resolve_local_config_path(v: &OmoVariant) -> Result<PathBuf, AppError> {
let config_path = Self::config_path(v);
if config_path.exists() {
return Ok(config_path);
}
let json_path = config_path.with_extension("json");
if json_path.exists() {
return Ok(json_path);
}
Err(AppError::OmoConfigNotFound)
Self::find_existing_config_path(v, &get_opencode_dir()).ok_or(AppError::OmoConfigNotFound)
}
fn read_jsonc_object(path: &Path) -> Result<Map<String, Value>, AppError> {
@@ -123,12 +136,18 @@ impl OmoService {
// ── Public API (variant-parameterized) ─────────────────
pub fn delete_config_file(v: &OmoVariant) -> Result<(), AppError> {
let config_path = Self::config_path(v);
if config_path.exists() {
std::fs::remove_file(&config_path).map_err(|e| AppError::io(&config_path, e))?;
log::info!("{} config file deleted: {config_path:?}", v.label);
let base_dir = get_opencode_dir();
let mut deleted_paths = Vec::new();
for config_path in Self::config_candidates(v, &base_dir) {
if config_path.exists() {
std::fs::remove_file(&config_path).map_err(|e| AppError::io(&config_path, e))?;
deleted_paths.push(config_path);
}
}
crate::opencode_config::remove_plugin_by_prefix(v.plugin_prefix)?;
if !deleted_paths.is_empty() {
log::info!("{} config files deleted: {deleted_paths:?}", v.label);
}
crate::opencode_config::remove_plugins_by_prefixes(v.plugin_prefixes)?;
Ok(())
}
@@ -451,4 +470,38 @@ mod tests {
assert!(obj.contains_key("agents"));
assert!(obj.contains_key("disabled_agents"));
}
#[test]
fn test_find_existing_config_prefers_new_name_over_old() {
let dir = tempfile::tempdir().unwrap();
let old_path = dir.path().join("oh-my-opencode.jsonc");
let new_path = dir.path().join("oh-my-openagent.jsonc");
// Create both old and new files
std::fs::write(&old_path, r#"{"agents":{}}"#).unwrap();
std::fs::write(&new_path, r#"{"agents":{}}"#).unwrap();
let found = OmoService::find_existing_config_path(&STANDARD, dir.path());
assert_eq!(
found.unwrap(),
new_path,
"When both old and new config files exist, the new name (oh-my-openagent) must be preferred"
);
}
#[test]
fn test_find_existing_config_falls_back_to_old_name() {
let dir = tempfile::tempdir().unwrap();
let old_path = dir.path().join("oh-my-opencode.jsonc");
// Only old file exists
std::fs::write(&old_path, r#"{"agents":{}}"#).unwrap();
let found = OmoService::find_existing_config_path(&STANDARD, dir.path());
assert_eq!(
found.unwrap(),
old_path,
"When only the old config file exists, it should still be found"
);
}
}
@@ -21,7 +21,6 @@ pub fn launch_terminal(
"kitty" => launch_kitty(command, cwd),
"wezterm" => launch_wezterm(command, cwd),
"alacritty" => launch_alacritty(command, cwd),
"tabby" => launch_tabby(command, cwd),
"custom" => launch_custom(command, cwd, custom_config),
_ => Err(format!("Unsupported terminal target: {target}")),
}
@@ -212,35 +211,6 @@ fn launch_alacritty(command: &str, cwd: Option<&str>) -> Result<(), String> {
}
}
fn launch_tabby(command: &str, cwd: Option<&str>) -> Result<(), String> {
let status = Command::new("open")
.args(build_tabby_args(command, cwd))
.status()
.map_err(|e| format!("Failed to launch Tabby: {e}"))?;
if status.success() {
Ok(())
} else {
Err("Failed to launch Tabby.".to_string())
}
}
fn build_tabby_args(command: &str, cwd: Option<&str>) -> Vec<String> {
let full_command = build_shell_command(command, cwd);
let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/zsh".to_string());
vec![
"-na".to_string(),
"Tabby".to_string(),
"--args".to_string(),
"run".to_string(),
shell,
"-l".to_string(),
"-c".to_string(),
full_command,
]
}
fn launch_custom(
command: &str,
cwd: Option<&str>,
@@ -335,44 +305,4 @@ mod tests {
"raw:echo foo\\\\\\\\bar\\npwd\\n"
);
}
#[test]
fn tabby_uses_login_shell_for_resume_commands() {
let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/zsh".to_string());
let args = build_tabby_args("claude --resume abc-123", Some("/tmp/project dir"));
assert_eq!(
args,
vec![
"-na",
"Tabby",
"--args",
"run",
&shell,
"-l",
"-c",
"cd \"/tmp/project dir\" && claude --resume abc-123",
]
);
}
#[test]
fn tabby_keeps_command_when_no_cwd_is_provided() {
let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/zsh".to_string());
let args = build_tabby_args("claude --resume abc-123", None);
assert_eq!(
args,
vec![
"-na",
"Tabby",
"--args",
"run",
&shell,
"-l",
"-c",
"claude --resume abc-123",
]
);
}
}
+3 -3
View File
@@ -264,9 +264,9 @@ pub struct AppSettings {
// ===== 终端设置 =====
/// 首选终端应用(可选,默认使用系统默认终端)
/// - macOS: "terminal" | "iterm2" | "alacritty" | "kitty" | "ghostty" | "wezterm" | "tabby"
/// - Windows: "cmd" | "powershell" | "wt" (Windows Terminal) | "tabby"
/// - Linux: "gnome-terminal" | "konsole" | "xfce4-terminal" | "alacritty" | "kitty" | "ghostty" | "tabby"
/// - macOS: "terminal" | "iterm2" | "warp" | "alacritty" | "kitty" | "ghostty"
/// - Windows: "cmd" | "powershell" | "wt" (Windows Terminal)
/// - Linux: "gnome-terminal" | "konsole" | "xfce4-terminal" | "alacritty" | "kitty" | "ghostty"
#[serde(default, skip_serializing_if = "Option::is_none")]
pub preferred_terminal: Option<String>,
}
@@ -65,7 +65,7 @@ export function ProviderPresetSelector({
case "omo":
return t("providerForm.omoHint", {
defaultValue:
"💡 OMO 配置管理 Agent 模型分配,写入 oh-my-opencode.jsonc",
"💡 OMO 配置管理 Agent 模型分配,兼容 oh-my-openagent.jsonc / oh-my-opencode.jsonc",
});
default:
return t("providerPreset.hint", {
@@ -16,7 +16,6 @@ const MACOS_TERMINALS = [
{ value: "kitty", labelKey: "settings.terminal.options.macos.kitty" },
{ value: "ghostty", labelKey: "settings.terminal.options.macos.ghostty" },
{ value: "wezterm", labelKey: "settings.terminal.options.macos.wezterm" },
{ value: "tabby", labelKey: "settings.terminal.options.macos.tabby" },
] as const;
const WINDOWS_TERMINALS = [
@@ -26,7 +25,6 @@ const WINDOWS_TERMINALS = [
labelKey: "settings.terminal.options.windows.powershell",
},
{ value: "wt", labelKey: "settings.terminal.options.windows.wt" },
{ value: "tabby", labelKey: "settings.terminal.options.windows.tabby" },
] as const;
const LINUX_TERMINALS = [
@@ -42,7 +40,6 @@ const LINUX_TERMINALS = [
{ value: "alacritty", labelKey: "settings.terminal.options.linux.alacritty" },
{ value: "kitty", labelKey: "settings.terminal.options.linux.kitty" },
{ value: "ghostty", labelKey: "settings.terminal.options.linux.ghostty" },
{ value: "tabby", labelKey: "settings.terminal.options.linux.tabby" },
] as const;
// Get terminals for the current platform
+1 -1
View File
@@ -1343,7 +1343,7 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [
{
name: "Oh My OpenCode",
websiteUrl: "https://github.com/code-yeongyu/oh-my-opencode",
websiteUrl: "https://github.com/code-yeongyu/oh-my-openagent",
settingsConfig: {
npm: "",
options: {},
+4 -7
View File
@@ -496,14 +496,12 @@
"alacritty": "Alacritty",
"kitty": "Kitty",
"ghostty": "Ghostty",
"wezterm": "WezTerm",
"tabby": "Tabby"
"wezterm": "WezTerm"
},
"windows": {
"cmd": "Command Prompt",
"powershell": "PowerShell",
"wt": "Windows Terminal",
"tabby": "Tabby"
"wt": "Windows Terminal"
},
"linux": {
"gnomeTerminal": "GNOME Terminal",
@@ -511,8 +509,7 @@
"xfce4Terminal": "Xfce4 Terminal",
"alacritty": "Alacritty",
"kitty": "Kitty",
"ghostty": "Ghostty",
"tabby": "Tabby"
"ghostty": "Ghostty"
}
}
},
@@ -701,7 +698,7 @@
"aggregatorApiKeyHint": "💡 Only need to fill in API Key, endpoint is preset",
"thirdPartyApiKeyHint": "💡 Only need to fill in API Key, endpoint is preset",
"customApiKeyHint": "💡 Custom configuration requires manually filling all necessary fields",
"omoHint": "💡 OMO config manages Agent model assignments and writes to oh-my-opencode.jsonc",
"omoHint": "💡 OMO config manages Agent model assignments and supports both oh-my-openagent.jsonc and oh-my-opencode.jsonc",
"officialHint": "💡 Official provider uses browser login, no API Key needed",
"getApiKey": "Get API Key",
"partnerPromotion": {
+4 -7
View File
@@ -496,14 +496,12 @@
"alacritty": "Alacritty",
"kitty": "Kitty",
"ghostty": "Ghostty",
"wezterm": "WezTerm",
"tabby": "Tabby"
"wezterm": "WezTerm"
},
"windows": {
"cmd": "コマンドプロンプト",
"powershell": "PowerShell",
"wt": "Windows Terminal",
"tabby": "Tabby"
"wt": "Windows Terminal"
},
"linux": {
"gnomeTerminal": "GNOME Terminal",
@@ -511,8 +509,7 @@
"xfce4Terminal": "Xfce4 Terminal",
"alacritty": "Alacritty",
"kitty": "Kitty",
"ghostty": "Ghostty",
"tabby": "Tabby"
"ghostty": "Ghostty"
}
}
},
@@ -701,7 +698,7 @@
"aggregatorApiKeyHint": "💡 API Key のみ入力すれば OK。エンドポイントはプリセット済みです",
"thirdPartyApiKeyHint": "💡 API Key のみ入力すれば OK。エンドポイントはプリセット済みです",
"customApiKeyHint": "💡 カスタム設定では必要な項目をすべて手動で入力してください",
"omoHint": "💡 OMO 設定は Agent のモデル割り当てを管理し、oh-my-opencode.jsonc に書き込みます",
"omoHint": "💡 OMO 設定は Agent のモデル割り当てを管理し、oh-my-openagent.jsonc / oh-my-opencode.jsonc の両方に対応します",
"officialHint": "💡 公式プロバイダーはブラウザログインで、API Key は不要です",
"getApiKey": "API Key を取得",
"partnerPromotion": {
+4 -7
View File
@@ -496,14 +496,12 @@
"alacritty": "Alacritty",
"kitty": "Kitty",
"ghostty": "Ghostty",
"wezterm": "WezTerm",
"tabby": "Tabby"
"wezterm": "WezTerm"
},
"windows": {
"cmd": "命令提示符",
"powershell": "PowerShell",
"wt": "Windows Terminal",
"tabby": "Tabby"
"wt": "Windows Terminal"
},
"linux": {
"gnomeTerminal": "GNOME Terminal",
@@ -511,8 +509,7 @@
"xfce4Terminal": "Xfce4 Terminal",
"alacritty": "Alacritty",
"kitty": "Kitty",
"ghostty": "Ghostty",
"tabby": "Tabby"
"ghostty": "Ghostty"
}
}
},
@@ -701,7 +698,7 @@
"aggregatorApiKeyHint": "💡 只需填写 API Key,请求地址已预设",
"thirdPartyApiKeyHint": "💡 只需填写 API Key,请求地址已预设",
"customApiKeyHint": "💡 自定义配置需手动填写所有必要字段",
"omoHint": "💡 OMO 配置管理 Agent 模型分配,写入 oh-my-opencode.jsonc",
"omoHint": "💡 OMO 配置管理 Agent 模型分配,兼容 oh-my-openagent.jsonc / oh-my-opencode.jsonc",
"officialHint": "💡 官方供应商使用浏览器登录,无需配置 API Key",
"getApiKey": "获取 API Key",
"partnerPromotion": {
+3 -3
View File
@@ -303,9 +303,9 @@ export interface Settings {
// ===== 终端设置 =====
// 首选终端应用(可选,默认使用系统默认终端)
// macOS: "terminal" | "iterm2" | "alacritty" | "kitty" | "ghostty" | "wezterm" | "tabby"
// Windows: "cmd" | "powershell" | "wt" | "tabby"
// Linux: "gnome-terminal" | "konsole" | "xfce4-terminal" | "alacritty" | "kitty" | "ghostty" | "tabby"
// macOS: "terminal" | "iterm2" | "warp" | "alacritty" | "kitty" | "ghostty"
// Windows: "cmd" | "powershell" | "wt"
// Linux: "gnome-terminal" | "konsole" | "xfce4-terminal" | "alacritty" | "kitty" | "ghostty"
preferredTerminal?: string;
}
+1 -1
View File
@@ -246,7 +246,7 @@ export const OMO_DISABLEABLE_SKILLS = [
] as const;
export const OMO_DEFAULT_SCHEMA_URL =
"https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json";
"https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json";
export const OMO_SISYPHUS_AGENT_PLACEHOLDER = `{
"disabled": false,