diff --git a/AGENTS.md b/AGENTS.md index d772c748..d80457fc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -274,6 +274,7 @@ Code map: │ ├── dotmatrix-hooks.ts │ └── stream.ts ├── lib + │ ├── editorTabs.ts │ ├── fileIcon.ts │ ├── ipc.ts │ ├── language.ts diff --git a/crates/sinew-app/src/lib.rs b/crates/sinew-app/src/lib.rs index 7b0ebe80..0ad904c5 100644 --- a/crates/sinew-app/src/lib.rs +++ b/crates/sinew-app/src/lib.rs @@ -36,8 +36,8 @@ pub use mcp::{probe_mcp_servers, McpServerProbe, McpSettings, McpToolRegistry}; pub use question::QuestionTool; pub use read::{ReadFingerprint, ReadTool}; pub use skill::{ - create_installed_skill, list_installed_skills, InstalledSkill, SkillConfig, SkillSettings, - SkillSource, SkillTool, + create_installed_skill, import_skills_from_provider, list_installed_skills, ImportSkillsResult, + InstalledSkill, SkillConfig, SkillSettings, SkillSource, SkillTool, SkippedSkillImport, }; pub use store::{ tool_settings_view, AppStore, ConversationSummary, GoalWorkflowState, ModeModelSettings, @@ -46,7 +46,9 @@ pub use store::{ WorkspaceBootstrap, DEFAULT_PLAN_MODE_PROMPT, }; pub use subagent::{ - is_subagent_tool_name, subagent_system_prompt, SubAgentConfig, SubAgentSettings, SubAgentTool, + import_sub_agents_from_provider, is_subagent_tool_name, subagent_system_prompt, + ImportSubAgentsResult, SkippedSubAgentImport, SubAgentConfig, SubAgentSettings, SubAgentSource, + SubAgentTool, }; pub use team::{is_team_tool_name, TeamRuntime, TeamTool}; pub use todo::{ diff --git a/crates/sinew-app/src/skill.rs b/crates/sinew-app/src/skill.rs index 4afef07f..372f3b36 100644 --- a/crates/sinew-app/src/skill.rs +++ b/crates/sinew-app/src/skill.rs @@ -323,7 +323,7 @@ pub fn list_installed_skills( description, source, root_label: root_label.clone(), - absolute_path: skill_path.display().to_string(), + absolute_path: display_path_string(&skill_path), content, enabled, }); @@ -394,6 +394,17 @@ fn default_skill_template(name: &str) -> String { ) } +fn display_path_string(path: &Path) -> String { + let raw = path.display().to_string(); + if let Some(rest) = raw.strip_prefix(r"\\?\UNC\") { + return format!(r"\\{rest}"); + } + if let Some(rest) = raw.strip_prefix(r"\\?\") { + return rest.to_string(); + } + raw +} + fn format_root_label(root: &Path, workspace_root: &Path, home_dir: Option<&Path>) -> String { if let Ok(rel) = root.strip_prefix(workspace_root) { return rel.display().to_string(); @@ -422,6 +433,154 @@ fn default_enabled() -> bool { true } +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SkippedSkillImport { + pub name: String, + pub reason: String, +} + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ImportSkillsResult { + pub imported: Vec, + pub skipped: Vec, +} + +/// Copy skills from Claude Code or Codex/ChatGPT install locations into +/// `/.agents/skills/`, which Sinew scans as workspace skills. +pub fn import_skills_from_provider( + workspace_root: impl AsRef, + provider: &str, +) -> Result { + let workspace_root = workspace_root.as_ref(); + let dest_base = workspace_root.join(".agents/skills"); + fs::create_dir_all(&dest_base) + .with_context(|| format!("unable to create {}", dest_base.display()))?; + + let home = BaseDirs::new() + .map(|base| base.home_dir().to_path_buf()) + .context("unable to locate home directory")?; + + let sources = provider_import_roots(provider, workspace_root, &home)?; + let dest_canon = canonicalize_lossy(&dest_base); + + let mut imported = Vec::new(); + let mut skipped = Vec::new(); + let mut seen_dirs = HashSet::new(); + + for source_root in sources { + if !source_root.is_dir() { + continue; + } + if canonicalize_lossy(&source_root) == dest_canon { + continue; + } + + for entry in scan_skill_root(&source_root) { + let Some(skill_dir) = entry.path.parent() else { + continue; + }; + let dir_key = canonicalize_lossy(skill_dir); + if !seen_dirs.insert(dir_key) { + continue; + } + + let folder_name = skill_dir + .file_name() + .and_then(|name| name.to_str()) + .unwrap_or(entry.name.as_str()); + if folder_name.starts_with('.') { + continue; + } + + let dest_dir = dest_base.join(folder_name); + if dest_dir.exists() { + skipped.push(SkippedSkillImport { + name: entry.name, + reason: format!( + "folder already exists at {}", + dest_dir.strip_prefix(workspace_root) + .unwrap_or(&dest_dir) + .display() + ), + }); + continue; + } + + match copy_dir_recursive(skill_dir, &dest_dir) { + Ok(()) => imported.push(folder_name.to_string()), + Err(err) => skipped.push(SkippedSkillImport { + name: entry.name, + reason: err.to_string(), + }), + } + } + } + + imported.sort_unstable(); + Ok(ImportSkillsResult { imported, skipped }) +} + +fn provider_import_roots( + provider: &str, + workspace_root: &Path, + home: &Path, +) -> Result> { + let mut roots = Vec::new(); + match provider.trim().to_ascii_lowercase().as_str() { + "claude" => { + push_existing_dir(&mut roots, home.join(".claude/skills")); + push_existing_dir(&mut roots, workspace_root.join(".claude/skills")); + } + "codex" | "chatgpt" | "openai" => { + push_existing_dir(&mut roots, home.join(".agents/skills")); + if let Ok(codex_home) = std::env::var("CODEX_HOME") { + let trimmed = codex_home.trim(); + if !trimmed.is_empty() { + push_existing_dir(&mut roots, PathBuf::from(trimmed).join("skills")); + } + } + push_existing_dir(&mut roots, home.join(".codex/skills")); + // workspace `.agents/skills` is the import destination — skip as source + push_existing_dir(&mut roots, workspace_root.join(".codex/skills")); + } + other => bail!("unknown skill import provider `{other}` (expected claude or codex)"), + } + Ok(roots) +} + +fn push_existing_dir(roots: &mut Vec, path: PathBuf) { + if path.is_dir() { + roots.push(path); + } +} + +fn canonicalize_lossy(path: &Path) -> PathBuf { + fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf()) +} + +fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<()> { + if !src.is_dir() { + bail!("{} is not a directory", src.display()); + } + fs::create_dir_all(dst) + .with_context(|| format!("unable to create {}", dst.display()))?; + for entry in fs::read_dir(src).with_context(|| format!("unable to read {}", src.display()))? { + let entry = entry?; + let file_type = entry.file_type()?; + let from = entry.path(); + let to = dst.join(entry.file_name()); + if file_type.is_dir() { + copy_dir_recursive(&from, &to)?; + } else { + fs::copy(&from, &to) + .with_context(|| format!("unable to copy {} to {}", from.display(), to.display()))?; + } + } + Ok(()) +} + #[derive(Debug, Deserialize)] struct SkillInput { name: String, diff --git a/crates/sinew-app/src/subagent.rs b/crates/sinew-app/src/subagent.rs index b78d6c60..65d704d4 100644 --- a/crates/sinew-app/src/subagent.rs +++ b/crates/sinew-app/src/subagent.rs @@ -1,4 +1,12 @@ -use std::{collections::HashMap, path::PathBuf, sync::Arc}; +use std::{ + collections::{HashMap, HashSet}, + fs, + path::{Path, PathBuf}, + sync::Arc, +}; + +use anyhow::{Context, Result, bail}; +use directories::BaseDirs; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; @@ -25,6 +33,17 @@ pub struct SubAgentConfig { pub model: ModelRef, #[serde(default = "default_enabled")] pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub source: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub source_path: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum SubAgentSource { + Workspace, + Global, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] @@ -380,3 +399,329 @@ fn escape_attr(value: &str) -> String { fn default_enabled() -> bool { true } + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SkippedSubAgentImport { + pub name: String, + pub reason: String, +} + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ImportSubAgentsResult { + pub imported: Vec, + pub skipped: Vec, +} + +struct AgentMarkdownFile { + name: String, + description: String, + prompt: String, + model: ModelRef, + source: SubAgentSource, + source_path: PathBuf, + id: String, +} + +/// Merge Claude Code sub-agent definitions from `.claude/agents/` into settings. +pub fn import_sub_agents_from_provider( + workspace_root: impl AsRef, + provider: &str, + current: &SubAgentSettings, + default_model: &ModelRef, +) -> Result<(SubAgentSettings, ImportSubAgentsResult)> { + match provider.trim().to_ascii_lowercase().as_str() { + "claude" => import_claude_sub_agents(workspace_root, current, default_model), + other => bail!("unknown sub-agent import provider `{other}` (expected claude)"), + } +} + +fn import_claude_sub_agents( + workspace_root: impl AsRef, + current: &SubAgentSettings, + default_model: &ModelRef, +) -> Result<(SubAgentSettings, ImportSubAgentsResult)> { + let workspace_root = workspace_root.as_ref(); + let home = BaseDirs::new() + .map(|base| base.home_dir().to_path_buf()) + .context("unable to locate home directory")?; + + let mut discovered = Vec::new(); + collect_claude_agent_files( + &workspace_root.join(".claude/agents"), + SubAgentSource::Workspace, + default_model, + &mut discovered, + )?; + collect_claude_agent_files( + &home.join(".claude/agents"), + SubAgentSource::Global, + default_model, + &mut discovered, + )?; + + let mut settings = current.clone(); + let mut imported = Vec::new(); + let mut skipped = Vec::new(); + let mut seen_ids = settings + .agents + .iter() + .map(|agent| agent.id.clone()) + .collect::>(); + let mut seen_names = settings + .agents + .iter() + .map(|agent| agent.name.to_ascii_lowercase()) + .collect::>(); + + for file in discovered { + if seen_ids.contains(&file.id) || seen_names.contains(&file.name.to_ascii_lowercase()) { + skipped.push(SkippedSubAgentImport { + name: file.name.clone(), + reason: "already configured in Sinew".into(), + }); + continue; + } + seen_ids.insert(file.id.clone()); + seen_names.insert(file.name.to_ascii_lowercase()); + imported.push(file.name.clone()); + settings.agents.push(SubAgentConfig { + id: file.id, + name: file.name, + description: file.description, + prompt: file.prompt, + model: file.model, + enabled: true, + source: Some(file.source), + source_path: Some(display_path_string(&file.source_path)), + }); + } + + imported.sort_unstable(); + Ok((settings.normalized(), ImportSubAgentsResult { imported, skipped })) +} + +fn collect_claude_agent_files( + root: &Path, + source: SubAgentSource, + default_model: &ModelRef, + out: &mut Vec, +) -> Result<()> { + if !root.is_dir() { + return Ok(()); + } + collect_claude_agent_files_recursive(root, source, default_model, out) +} + +fn collect_claude_agent_files_recursive( + dir: &Path, + source: SubAgentSource, + default_model: &ModelRef, + out: &mut Vec, +) -> Result<()> { + for entry in fs::read_dir(dir).with_context(|| format!("unable to read {}", dir.display()))? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + collect_claude_agent_files_recursive(&path, source, default_model, out)?; + continue; + } + if path.extension().and_then(|ext| ext.to_str()) != Some("md") { + continue; + } + let Some(parsed) = parse_claude_agent_file(&path, source, default_model) else { + continue; + }; + out.push(parsed); + } + Ok(()) +} + +fn parse_claude_agent_file( + path: &Path, + source: SubAgentSource, + fallback_model: &ModelRef, +) -> Option { + let content = fs::read_to_string(path).ok()?; + let frontmatter = parse_frontmatter(&content); + let fallback_name = path + .file_stem() + .and_then(|name| name.to_str()) + .unwrap_or("agent") + .to_string(); + let name = frontmatter + .get("name") + .map(|value| value.trim().to_string()) + .filter(|value| !value.is_empty()) + .unwrap_or(fallback_name); + let description = frontmatter + .get("description") + .map(|value| value.trim().to_string()) + .unwrap_or_default(); + let prompt = strip_frontmatter(&content).trim().to_string(); + if prompt.is_empty() && description.is_empty() { + return None; + } + let model = frontmatter + .get("model") + .map(|value| map_claude_model_alias(value, &fallback_model)) + .unwrap_or_else(|| fallback_model.clone()); + let id = claude_agent_id(&name); + Some(AgentMarkdownFile { + name, + description, + prompt, + model, + source, + source_path: path.to_path_buf(), + id, + }) +} + +fn map_claude_model_alias(alias: &str, default_model: &ModelRef) -> ModelRef { + let trimmed = alias.trim(); + if trimmed.is_empty() || trimmed.eq_ignore_ascii_case("inherit") { + return default_model.clone(); + } + let lower = trimmed.to_ascii_lowercase(); + if lower.contains("opus") { + return ModelRef::new("anthropic", "claude-opus-4-8"); + } + if lower.contains("sonnet") { + return ModelRef::new("anthropic", "claude-sonnet-4-6"); + } + if lower.contains("haiku") { + return ModelRef::new("anthropic", "claude-haiku-4-5"); + } + if lower.contains("gpt") || lower.contains("codex") { + return ModelRef::new("openai", "gpt-5.5"); + } + default_model.clone() +} + +fn claude_agent_id(name: &str) -> String { + let mut slug = String::new(); + for ch in name.trim().to_ascii_lowercase().chars() { + if ch.is_ascii_alphanumeric() { + slug.push(ch); + } else if (ch == '-' || ch == '_') && !slug.ends_with('-') { + slug.push('-'); + } else if !slug.is_empty() && !slug.ends_with('-') { + slug.push('-'); + } + } + let slug = slug.trim_matches('-').to_string(); + if slug.is_empty() { + "agent".to_string() + } else { + slug + } +} + +fn strip_frontmatter(content: &str) -> String { + let mut lines = content.lines(); + if lines.next().map(str::trim) != Some("---") { + return content.to_string(); + } + for line in lines.by_ref() { + if line.trim() == "---" { + return lines.collect::>().join("\n"); + } + } + content.to_string() +} + +fn parse_frontmatter(content: &str) -> HashMap { + let mut fields = HashMap::new(); + let mut lines = content.lines(); + if lines.next().map(str::trim) != Some("---") { + return fields; + } + for line in lines { + let trimmed = line.trim(); + if trimmed == "---" { + break; + } + let Some((key, value)) = trimmed.split_once(':') else { + continue; + }; + fields.insert( + key.trim().to_string(), + clean_yaml_string(value.trim()).to_string(), + ); + } + fields +} + +fn clean_yaml_string(value: &str) -> &str { + value + .strip_prefix('"') + .and_then(|value| value.strip_suffix('"')) + .or_else(|| { + value + .strip_prefix('\'') + .and_then(|value| value.strip_suffix('\'')) + }) + .unwrap_or(value) +} + +fn display_path_string(path: &Path) -> String { + let raw = path.display().to_string(); + if let Some(rest) = raw.strip_prefix(r"\\?\UNC\") { + return format!(r"\\{rest}"); + } + if let Some(rest) = raw.strip_prefix(r"\\?\") { + return rest.to_string(); + } + raw +} + +#[cfg(test)] +mod import_tests { + use super::*; + use std::fs; + + #[test] + fn maps_claude_model_aliases() { + let default = ModelRef::new("anthropic", "claude-sonnet-4-6"); + assert_eq!( + map_claude_model_alias("opus", &default).name, + "claude-opus-4-8" + ); + assert_eq!( + map_claude_model_alias("sonnet", &default).name, + "claude-sonnet-4-6" + ); + assert_eq!( + map_claude_model_alias("inherit", &default).name, + default.name + ); + } + + #[test] + fn parses_claude_agent_markdown() { + let dir = std::env::temp_dir().join(format!( + "sinew-agent-{}", + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_nanos()) + .unwrap_or(0) + )); + fs::create_dir_all(&dir).unwrap(); + let path = dir.join("reviewer.md"); + fs::write( + &path, + "---\nname: code-reviewer\ndescription: Reviews diffs\nmodel: opus\n---\n\nYou review code.\n", + ) + .unwrap(); + let default = ModelRef::new("anthropic", "claude-sonnet-4-6"); + let parsed = + parse_claude_agent_file(&path, SubAgentSource::Workspace, &default).unwrap(); + assert_eq!(parsed.name, "code-reviewer"); + assert_eq!(parsed.description, "Reviews diffs"); + assert!(parsed.prompt.contains("You review code")); + assert_eq!(parsed.id, "code-reviewer"); + let _ = fs::remove_dir_all(dir); + } +} diff --git a/crates/sinew-app/src/team/render.rs b/crates/sinew-app/src/team/render.rs index 50dc9bfd..5737cac1 100644 --- a/crates/sinew-app/src/team/render.rs +++ b/crates/sinew-app/src/team/render.rs @@ -8,6 +8,8 @@ pub(super) fn team_agent_system_prompt(base: &str, team_name: &str, agent: &Team prompt: agent.prompt.clone(), model: agent.model.clone(), enabled: true, + source: None, + source_path: None, }; let base = subagent_system_prompt(base, &config_agent); format!( diff --git a/src-tauri/gen/schemas/windows-schema.json b/src-tauri/gen/schemas/windows-schema.json new file mode 100644 index 00000000..ec78c04a --- /dev/null +++ b/src-tauri/gen/schemas/windows-schema.json @@ -0,0 +1,2364 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CapabilityFile", + "description": "Capability formats accepted in a capability file.", + "anyOf": [ + { + "description": "A single capability.", + "allOf": [ + { + "$ref": "#/definitions/Capability" + } + ] + }, + { + "description": "A list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + }, + { + "description": "A list of capabilities.", + "type": "object", + "required": [ + "capabilities" + ], + "properties": { + "capabilities": { + "description": "The list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + } + } + } + ], + "definitions": { + "Capability": { + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "type": "object", + "required": [ + "identifier", + "permissions" + ], + "properties": { + "identifier": { + "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", + "type": "string" + }, + "description": { + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.", + "default": "", + "type": "string" + }, + "remote": { + "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", + "anyOf": [ + { + "$ref": "#/definitions/CapabilityRemote" + }, + { + "type": "null" + } + ] + }, + "local": { + "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", + "default": true, + "type": "boolean" + }, + "windows": { + "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nIf a window label matches any of the patterns in this list, the capability will be enabled on all the webviews of that window, regardless of the value of [`Self::webviews`].\n\nOn multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "webviews": { + "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThe capability will be enabled on all the webviews whose label matches any of the patterns in this list, regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionEntry" + }, + "uniqueItems": true + }, + "platforms": { + "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Target" + } + } + } + }, + "CapabilityRemote": { + "description": "Configuration for remote URLs that are associated with the capability.", + "type": "object", + "required": [ + "urls" + ], + "properties": { + "urls": { + "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PermissionEntry": { + "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", + "anyOf": [ + { + "description": "Reference a permission or permission set by identifier.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + { + "description": "Reference a permission or permission set by identifier and extends its scope.", + "type": "object", + "allOf": [ + { + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + "allow": { + "description": "Data that defines what is allowed by the scope.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + }, + "deny": { + "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + } + } + } + ], + "required": [ + "identifier" + ] + } + ] + }, + "Identifier": { + "description": "Permission identifier", + "oneOf": [ + { + "description": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`", + "type": "string", + "const": "core:default", + "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`" + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`", + "type": "string", + "const": "core:app:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`" + }, + { + "description": "Enables the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-hide", + "markdownDescription": "Enables the app_hide command without any pre-configured scope." + }, + { + "description": "Enables the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-show", + "markdownDescription": "Enables the app_show command without any pre-configured scope." + }, + { + "description": "Enables the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-bundle-type", + "markdownDescription": "Enables the bundle_type command without any pre-configured scope." + }, + { + "description": "Enables the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-default-window-icon", + "markdownDescription": "Enables the default_window_icon command without any pre-configured scope." + }, + { + "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-fetch-data-store-identifiers", + "markdownDescription": "Enables the fetch_data_store_identifiers command without any pre-configured scope." + }, + { + "description": "Enables the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-identifier", + "markdownDescription": "Enables the identifier command without any pre-configured scope." + }, + { + "description": "Enables the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-name", + "markdownDescription": "Enables the name command without any pre-configured scope." + }, + { + "description": "Enables the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-register-listener", + "markdownDescription": "Enables the register_listener command without any pre-configured scope." + }, + { + "description": "Enables the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-data-store", + "markdownDescription": "Enables the remove_data_store command without any pre-configured scope." + }, + { + "description": "Enables the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-listener", + "markdownDescription": "Enables the remove_listener command without any pre-configured scope." + }, + { + "description": "Enables the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-app-theme", + "markdownDescription": "Enables the set_app_theme command without any pre-configured scope." + }, + { + "description": "Enables the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-dock-visibility", + "markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope." + }, + { + "description": "Enables the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-tauri-version", + "markdownDescription": "Enables the tauri_version command without any pre-configured scope." + }, + { + "description": "Enables the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-version", + "markdownDescription": "Enables the version command without any pre-configured scope." + }, + { + "description": "Denies the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-hide", + "markdownDescription": "Denies the app_hide command without any pre-configured scope." + }, + { + "description": "Denies the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-show", + "markdownDescription": "Denies the app_show command without any pre-configured scope." + }, + { + "description": "Denies the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-bundle-type", + "markdownDescription": "Denies the bundle_type command without any pre-configured scope." + }, + { + "description": "Denies the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-default-window-icon", + "markdownDescription": "Denies the default_window_icon command without any pre-configured scope." + }, + { + "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-fetch-data-store-identifiers", + "markdownDescription": "Denies the fetch_data_store_identifiers command without any pre-configured scope." + }, + { + "description": "Denies the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-identifier", + "markdownDescription": "Denies the identifier command without any pre-configured scope." + }, + { + "description": "Denies the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-name", + "markdownDescription": "Denies the name command without any pre-configured scope." + }, + { + "description": "Denies the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-register-listener", + "markdownDescription": "Denies the register_listener command without any pre-configured scope." + }, + { + "description": "Denies the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-data-store", + "markdownDescription": "Denies the remove_data_store command without any pre-configured scope." + }, + { + "description": "Denies the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-listener", + "markdownDescription": "Denies the remove_listener command without any pre-configured scope." + }, + { + "description": "Denies the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-app-theme", + "markdownDescription": "Denies the set_app_theme command without any pre-configured scope." + }, + { + "description": "Denies the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-dock-visibility", + "markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope." + }, + { + "description": "Denies the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-tauri-version", + "markdownDescription": "Denies the tauri_version command without any pre-configured scope." + }, + { + "description": "Denies the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-version", + "markdownDescription": "Denies the version command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`", + "type": "string", + "const": "core:event:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`" + }, + { + "description": "Enables the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit", + "markdownDescription": "Enables the emit command without any pre-configured scope." + }, + { + "description": "Enables the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit-to", + "markdownDescription": "Enables the emit_to command without any pre-configured scope." + }, + { + "description": "Enables the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-listen", + "markdownDescription": "Enables the listen command without any pre-configured scope." + }, + { + "description": "Enables the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-unlisten", + "markdownDescription": "Enables the unlisten command without any pre-configured scope." + }, + { + "description": "Denies the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit", + "markdownDescription": "Denies the emit command without any pre-configured scope." + }, + { + "description": "Denies the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit-to", + "markdownDescription": "Denies the emit_to command without any pre-configured scope." + }, + { + "description": "Denies the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-listen", + "markdownDescription": "Denies the listen command without any pre-configured scope." + }, + { + "description": "Denies the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-unlisten", + "markdownDescription": "Denies the unlisten command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`", + "type": "string", + "const": "core:image:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`" + }, + { + "description": "Enables the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-bytes", + "markdownDescription": "Enables the from_bytes command without any pre-configured scope." + }, + { + "description": "Enables the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-path", + "markdownDescription": "Enables the from_path command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-rgba", + "markdownDescription": "Enables the rgba command without any pre-configured scope." + }, + { + "description": "Enables the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-size", + "markdownDescription": "Enables the size command without any pre-configured scope." + }, + { + "description": "Denies the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-bytes", + "markdownDescription": "Denies the from_bytes command without any pre-configured scope." + }, + { + "description": "Denies the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-path", + "markdownDescription": "Denies the from_path command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-rgba", + "markdownDescription": "Denies the rgba command without any pre-configured scope." + }, + { + "description": "Denies the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-size", + "markdownDescription": "Denies the size command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`", + "type": "string", + "const": "core:menu:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`" + }, + { + "description": "Enables the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-append", + "markdownDescription": "Enables the append command without any pre-configured scope." + }, + { + "description": "Enables the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-create-default", + "markdownDescription": "Enables the create_default command without any pre-configured scope." + }, + { + "description": "Enables the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-get", + "markdownDescription": "Enables the get command without any pre-configured scope." + }, + { + "description": "Enables the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-insert", + "markdownDescription": "Enables the insert command without any pre-configured scope." + }, + { + "description": "Enables the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-checked", + "markdownDescription": "Enables the is_checked command without any pre-configured scope." + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." + }, + { + "description": "Enables the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-items", + "markdownDescription": "Enables the items command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-popup", + "markdownDescription": "Enables the popup command without any pre-configured scope." + }, + { + "description": "Enables the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-prepend", + "markdownDescription": "Enables the prepend command without any pre-configured scope." + }, + { + "description": "Enables the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove", + "markdownDescription": "Enables the remove command without any pre-configured scope." + }, + { + "description": "Enables the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove-at", + "markdownDescription": "Enables the remove_at command without any pre-configured scope." + }, + { + "description": "Enables the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-accelerator", + "markdownDescription": "Enables the set_accelerator command without any pre-configured scope." + }, + { + "description": "Enables the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-app-menu", + "markdownDescription": "Enables the set_as_app_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-help-menu-for-nsapp", + "markdownDescription": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Enables the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-window-menu", + "markdownDescription": "Enables the set_as_window_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-windows-menu-for-nsapp", + "markdownDescription": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Enables the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-checked", + "markdownDescription": "Enables the set_checked command without any pre-configured scope." + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-text", + "markdownDescription": "Enables the set_text command without any pre-configured scope." + }, + { + "description": "Enables the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-text", + "markdownDescription": "Enables the text command without any pre-configured scope." + }, + { + "description": "Denies the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-append", + "markdownDescription": "Denies the append command without any pre-configured scope." + }, + { + "description": "Denies the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-create-default", + "markdownDescription": "Denies the create_default command without any pre-configured scope." + }, + { + "description": "Denies the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-get", + "markdownDescription": "Denies the get command without any pre-configured scope." + }, + { + "description": "Denies the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-insert", + "markdownDescription": "Denies the insert command without any pre-configured scope." + }, + { + "description": "Denies the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-checked", + "markdownDescription": "Denies the is_checked command without any pre-configured scope." + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." + }, + { + "description": "Denies the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-items", + "markdownDescription": "Denies the items command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-popup", + "markdownDescription": "Denies the popup command without any pre-configured scope." + }, + { + "description": "Denies the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-prepend", + "markdownDescription": "Denies the prepend command without any pre-configured scope." + }, + { + "description": "Denies the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove", + "markdownDescription": "Denies the remove command without any pre-configured scope." + }, + { + "description": "Denies the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove-at", + "markdownDescription": "Denies the remove_at command without any pre-configured scope." + }, + { + "description": "Denies the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-accelerator", + "markdownDescription": "Denies the set_accelerator command without any pre-configured scope." + }, + { + "description": "Denies the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-app-menu", + "markdownDescription": "Denies the set_as_app_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-help-menu-for-nsapp", + "markdownDescription": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Denies the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-window-menu", + "markdownDescription": "Denies the set_as_window_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-windows-menu-for-nsapp", + "markdownDescription": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Denies the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-checked", + "markdownDescription": "Denies the set_checked command without any pre-configured scope." + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-text", + "markdownDescription": "Denies the set_text command without any pre-configured scope." + }, + { + "description": "Denies the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-text", + "markdownDescription": "Denies the text command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`", + "type": "string", + "const": "core:path:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`" + }, + { + "description": "Enables the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-basename", + "markdownDescription": "Enables the basename command without any pre-configured scope." + }, + { + "description": "Enables the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-dirname", + "markdownDescription": "Enables the dirname command without any pre-configured scope." + }, + { + "description": "Enables the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-extname", + "markdownDescription": "Enables the extname command without any pre-configured scope." + }, + { + "description": "Enables the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-is-absolute", + "markdownDescription": "Enables the is_absolute command without any pre-configured scope." + }, + { + "description": "Enables the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-join", + "markdownDescription": "Enables the join command without any pre-configured scope." + }, + { + "description": "Enables the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-normalize", + "markdownDescription": "Enables the normalize command without any pre-configured scope." + }, + { + "description": "Enables the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve", + "markdownDescription": "Enables the resolve command without any pre-configured scope." + }, + { + "description": "Enables the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve-directory", + "markdownDescription": "Enables the resolve_directory command without any pre-configured scope." + }, + { + "description": "Denies the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-basename", + "markdownDescription": "Denies the basename command without any pre-configured scope." + }, + { + "description": "Denies the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-dirname", + "markdownDescription": "Denies the dirname command without any pre-configured scope." + }, + { + "description": "Denies the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-extname", + "markdownDescription": "Denies the extname command without any pre-configured scope." + }, + { + "description": "Denies the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-is-absolute", + "markdownDescription": "Denies the is_absolute command without any pre-configured scope." + }, + { + "description": "Denies the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-join", + "markdownDescription": "Denies the join command without any pre-configured scope." + }, + { + "description": "Denies the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-normalize", + "markdownDescription": "Denies the normalize command without any pre-configured scope." + }, + { + "description": "Denies the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve", + "markdownDescription": "Denies the resolve command without any pre-configured scope." + }, + { + "description": "Denies the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve-directory", + "markdownDescription": "Denies the resolve_directory command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`", + "type": "string", + "const": "core:resources:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`", + "type": "string", + "const": "core:tray:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`" + }, + { + "description": "Enables the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-get-by-id", + "markdownDescription": "Enables the get_by_id command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-remove-by-id", + "markdownDescription": "Enables the remove_by_id command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon-as-template", + "markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope." + }, + { + "description": "Enables the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-menu", + "markdownDescription": "Enables the set_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-show-menu-on-left-click", + "markdownDescription": "Enables the set_show_menu_on_left_click command without any pre-configured scope." + }, + { + "description": "Enables the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-temp-dir-path", + "markdownDescription": "Enables the set_temp_dir_path command without any pre-configured scope." + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." + }, + { + "description": "Enables the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-tooltip", + "markdownDescription": "Enables the set_tooltip command without any pre-configured scope." + }, + { + "description": "Enables the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-visible", + "markdownDescription": "Enables the set_visible command without any pre-configured scope." + }, + { + "description": "Denies the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-get-by-id", + "markdownDescription": "Denies the get_by_id command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-remove-by-id", + "markdownDescription": "Denies the remove_by_id command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon-as-template", + "markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope." + }, + { + "description": "Denies the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-menu", + "markdownDescription": "Denies the set_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-show-menu-on-left-click", + "markdownDescription": "Denies the set_show_menu_on_left_click command without any pre-configured scope." + }, + { + "description": "Denies the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-temp-dir-path", + "markdownDescription": "Denies the set_temp_dir_path command without any pre-configured scope." + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." + }, + { + "description": "Denies the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-tooltip", + "markdownDescription": "Denies the set_tooltip command without any pre-configured scope." + }, + { + "description": "Denies the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-visible", + "markdownDescription": "Denies the set_visible command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`", + "type": "string", + "const": "core:webview:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`" + }, + { + "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-clear-all-browsing-data", + "markdownDescription": "Enables the clear_all_browsing_data command without any pre-configured scope." + }, + { + "description": "Enables the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview", + "markdownDescription": "Enables the create_webview command without any pre-configured scope." + }, + { + "description": "Enables the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview-window", + "markdownDescription": "Enables the create_webview_window command without any pre-configured scope." + }, + { + "description": "Enables the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-get-all-webviews", + "markdownDescription": "Enables the get_all_webviews command without any pre-configured scope." + }, + { + "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-internal-toggle-devtools", + "markdownDescription": "Enables the internal_toggle_devtools command without any pre-configured scope." + }, + { + "description": "Enables the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-print", + "markdownDescription": "Enables the print command without any pre-configured scope." + }, + { + "description": "Enables the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-reparent", + "markdownDescription": "Enables the reparent command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-auto-resize", + "markdownDescription": "Enables the set_webview_auto_resize command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-background-color", + "markdownDescription": "Enables the set_webview_background_color command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-focus", + "markdownDescription": "Enables the set_webview_focus command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-position", + "markdownDescription": "Enables the set_webview_position command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-size", + "markdownDescription": "Enables the set_webview_size command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-zoom", + "markdownDescription": "Enables the set_webview_zoom command without any pre-configured scope." + }, + { + "description": "Enables the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-close", + "markdownDescription": "Enables the webview_close command without any pre-configured scope." + }, + { + "description": "Enables the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-hide", + "markdownDescription": "Enables the webview_hide command without any pre-configured scope." + }, + { + "description": "Enables the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-position", + "markdownDescription": "Enables the webview_position command without any pre-configured scope." + }, + { + "description": "Enables the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-show", + "markdownDescription": "Enables the webview_show command without any pre-configured scope." + }, + { + "description": "Enables the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-size", + "markdownDescription": "Enables the webview_size command without any pre-configured scope." + }, + { + "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-clear-all-browsing-data", + "markdownDescription": "Denies the clear_all_browsing_data command without any pre-configured scope." + }, + { + "description": "Denies the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview", + "markdownDescription": "Denies the create_webview command without any pre-configured scope." + }, + { + "description": "Denies the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview-window", + "markdownDescription": "Denies the create_webview_window command without any pre-configured scope." + }, + { + "description": "Denies the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-get-all-webviews", + "markdownDescription": "Denies the get_all_webviews command without any pre-configured scope." + }, + { + "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-internal-toggle-devtools", + "markdownDescription": "Denies the internal_toggle_devtools command without any pre-configured scope." + }, + { + "description": "Denies the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-print", + "markdownDescription": "Denies the print command without any pre-configured scope." + }, + { + "description": "Denies the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-reparent", + "markdownDescription": "Denies the reparent command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-auto-resize", + "markdownDescription": "Denies the set_webview_auto_resize command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-background-color", + "markdownDescription": "Denies the set_webview_background_color command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-focus", + "markdownDescription": "Denies the set_webview_focus command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-position", + "markdownDescription": "Denies the set_webview_position command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-size", + "markdownDescription": "Denies the set_webview_size command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-zoom", + "markdownDescription": "Denies the set_webview_zoom command without any pre-configured scope." + }, + { + "description": "Denies the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-close", + "markdownDescription": "Denies the webview_close command without any pre-configured scope." + }, + { + "description": "Denies the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-hide", + "markdownDescription": "Denies the webview_hide command without any pre-configured scope." + }, + { + "description": "Denies the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-position", + "markdownDescription": "Denies the webview_position command without any pre-configured scope." + }, + { + "description": "Denies the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-show", + "markdownDescription": "Denies the webview_show command without any pre-configured scope." + }, + { + "description": "Denies the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-size", + "markdownDescription": "Denies the webview_size command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`", + "type": "string", + "const": "core:window:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`" + }, + { + "description": "Enables the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-available-monitors", + "markdownDescription": "Enables the available_monitors command without any pre-configured scope." + }, + { + "description": "Enables the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-center", + "markdownDescription": "Enables the center command without any pre-configured scope." + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." + }, + { + "description": "Enables the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-create", + "markdownDescription": "Enables the create command without any pre-configured scope." + }, + { + "description": "Enables the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-current-monitor", + "markdownDescription": "Enables the current_monitor command without any pre-configured scope." + }, + { + "description": "Enables the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-cursor-position", + "markdownDescription": "Enables the cursor_position command without any pre-configured scope." + }, + { + "description": "Enables the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-destroy", + "markdownDescription": "Enables the destroy command without any pre-configured scope." + }, + { + "description": "Enables the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-get-all-windows", + "markdownDescription": "Enables the get_all_windows command without any pre-configured scope." + }, + { + "description": "Enables the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-hide", + "markdownDescription": "Enables the hide command without any pre-configured scope." + }, + { + "description": "Enables the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-position", + "markdownDescription": "Enables the inner_position command without any pre-configured scope." + }, + { + "description": "Enables the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-size", + "markdownDescription": "Enables the inner_size command without any pre-configured scope." + }, + { + "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-internal-toggle-maximize", + "markdownDescription": "Enables the internal_toggle_maximize command without any pre-configured scope." + }, + { + "description": "Enables the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-always-on-top", + "markdownDescription": "Enables the is_always_on_top command without any pre-configured scope." + }, + { + "description": "Enables the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-closable", + "markdownDescription": "Enables the is_closable command without any pre-configured scope." + }, + { + "description": "Enables the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-decorated", + "markdownDescription": "Enables the is_decorated command without any pre-configured scope." + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." + }, + { + "description": "Enables the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-focused", + "markdownDescription": "Enables the is_focused command without any pre-configured scope." + }, + { + "description": "Enables the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-fullscreen", + "markdownDescription": "Enables the is_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximizable", + "markdownDescription": "Enables the is_maximizable command without any pre-configured scope." + }, + { + "description": "Enables the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximized", + "markdownDescription": "Enables the is_maximized command without any pre-configured scope." + }, + { + "description": "Enables the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimizable", + "markdownDescription": "Enables the is_minimizable command without any pre-configured scope." + }, + { + "description": "Enables the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimized", + "markdownDescription": "Enables the is_minimized command without any pre-configured scope." + }, + { + "description": "Enables the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-resizable", + "markdownDescription": "Enables the is_resizable command without any pre-configured scope." + }, + { + "description": "Enables the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-visible", + "markdownDescription": "Enables the is_visible command without any pre-configured scope." + }, + { + "description": "Enables the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-maximize", + "markdownDescription": "Enables the maximize command without any pre-configured scope." + }, + { + "description": "Enables the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-minimize", + "markdownDescription": "Enables the minimize command without any pre-configured scope." + }, + { + "description": "Enables the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-monitor-from-point", + "markdownDescription": "Enables the monitor_from_point command without any pre-configured scope." + }, + { + "description": "Enables the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-position", + "markdownDescription": "Enables the outer_position command without any pre-configured scope." + }, + { + "description": "Enables the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-size", + "markdownDescription": "Enables the outer_size command without any pre-configured scope." + }, + { + "description": "Enables the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-primary-monitor", + "markdownDescription": "Enables the primary_monitor command without any pre-configured scope." + }, + { + "description": "Enables the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-request-user-attention", + "markdownDescription": "Enables the request_user_attention command without any pre-configured scope." + }, + { + "description": "Enables the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-scale-factor", + "markdownDescription": "Enables the scale_factor command without any pre-configured scope." + }, + { + "description": "Enables the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-bottom", + "markdownDescription": "Enables the set_always_on_bottom command without any pre-configured scope." + }, + { + "description": "Enables the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-top", + "markdownDescription": "Enables the set_always_on_top command without any pre-configured scope." + }, + { + "description": "Enables the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-background-color", + "markdownDescription": "Enables the set_background_color command without any pre-configured scope." + }, + { + "description": "Enables the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-count", + "markdownDescription": "Enables the set_badge_count command without any pre-configured scope." + }, + { + "description": "Enables the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-label", + "markdownDescription": "Enables the set_badge_label command without any pre-configured scope." + }, + { + "description": "Enables the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-closable", + "markdownDescription": "Enables the set_closable command without any pre-configured scope." + }, + { + "description": "Enables the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-content-protected", + "markdownDescription": "Enables the set_content_protected command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-grab", + "markdownDescription": "Enables the set_cursor_grab command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-icon", + "markdownDescription": "Enables the set_cursor_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-position", + "markdownDescription": "Enables the set_cursor_position command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-visible", + "markdownDescription": "Enables the set_cursor_visible command without any pre-configured scope." + }, + { + "description": "Enables the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-decorations", + "markdownDescription": "Enables the set_decorations command without any pre-configured scope." + }, + { + "description": "Enables the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-effects", + "markdownDescription": "Enables the set_effects command without any pre-configured scope." + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." + }, + { + "description": "Enables the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focus", + "markdownDescription": "Enables the set_focus command without any pre-configured scope." + }, + { + "description": "Enables the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focusable", + "markdownDescription": "Enables the set_focusable command without any pre-configured scope." + }, + { + "description": "Enables the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-fullscreen", + "markdownDescription": "Enables the set_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-ignore-cursor-events", + "markdownDescription": "Enables the set_ignore_cursor_events command without any pre-configured scope." + }, + { + "description": "Enables the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-max-size", + "markdownDescription": "Enables the set_max_size command without any pre-configured scope." + }, + { + "description": "Enables the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-maximizable", + "markdownDescription": "Enables the set_maximizable command without any pre-configured scope." + }, + { + "description": "Enables the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-min-size", + "markdownDescription": "Enables the set_min_size command without any pre-configured scope." + }, + { + "description": "Enables the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-minimizable", + "markdownDescription": "Enables the set_minimizable command without any pre-configured scope." + }, + { + "description": "Enables the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-overlay-icon", + "markdownDescription": "Enables the set_overlay_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-position", + "markdownDescription": "Enables the set_position command without any pre-configured scope." + }, + { + "description": "Enables the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-progress-bar", + "markdownDescription": "Enables the set_progress_bar command without any pre-configured scope." + }, + { + "description": "Enables the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-resizable", + "markdownDescription": "Enables the set_resizable command without any pre-configured scope." + }, + { + "description": "Enables the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-shadow", + "markdownDescription": "Enables the set_shadow command without any pre-configured scope." + }, + { + "description": "Enables the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-simple-fullscreen", + "markdownDescription": "Enables the set_simple_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size", + "markdownDescription": "Enables the set_size command without any pre-configured scope." + }, + { + "description": "Enables the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size-constraints", + "markdownDescription": "Enables the set_size_constraints command without any pre-configured scope." + }, + { + "description": "Enables the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-skip-taskbar", + "markdownDescription": "Enables the set_skip_taskbar command without any pre-configured scope." + }, + { + "description": "Enables the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-theme", + "markdownDescription": "Enables the set_theme command without any pre-configured scope." + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." + }, + { + "description": "Enables the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title-bar-style", + "markdownDescription": "Enables the set_title_bar_style command without any pre-configured scope." + }, + { + "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-visible-on-all-workspaces", + "markdownDescription": "Enables the set_visible_on_all_workspaces command without any pre-configured scope." + }, + { + "description": "Enables the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-show", + "markdownDescription": "Enables the show command without any pre-configured scope." + }, + { + "description": "Enables the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-dragging", + "markdownDescription": "Enables the start_dragging command without any pre-configured scope." + }, + { + "description": "Enables the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-resize-dragging", + "markdownDescription": "Enables the start_resize_dragging command without any pre-configured scope." + }, + { + "description": "Enables the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-theme", + "markdownDescription": "Enables the theme command without any pre-configured scope." + }, + { + "description": "Enables the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-title", + "markdownDescription": "Enables the title command without any pre-configured scope." + }, + { + "description": "Enables the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-toggle-maximize", + "markdownDescription": "Enables the toggle_maximize command without any pre-configured scope." + }, + { + "description": "Enables the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unmaximize", + "markdownDescription": "Enables the unmaximize command without any pre-configured scope." + }, + { + "description": "Enables the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unminimize", + "markdownDescription": "Enables the unminimize command without any pre-configured scope." + }, + { + "description": "Denies the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-available-monitors", + "markdownDescription": "Denies the available_monitors command without any pre-configured scope." + }, + { + "description": "Denies the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-center", + "markdownDescription": "Denies the center command without any pre-configured scope." + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." + }, + { + "description": "Denies the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-create", + "markdownDescription": "Denies the create command without any pre-configured scope." + }, + { + "description": "Denies the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-current-monitor", + "markdownDescription": "Denies the current_monitor command without any pre-configured scope." + }, + { + "description": "Denies the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-cursor-position", + "markdownDescription": "Denies the cursor_position command without any pre-configured scope." + }, + { + "description": "Denies the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-destroy", + "markdownDescription": "Denies the destroy command without any pre-configured scope." + }, + { + "description": "Denies the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-get-all-windows", + "markdownDescription": "Denies the get_all_windows command without any pre-configured scope." + }, + { + "description": "Denies the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-hide", + "markdownDescription": "Denies the hide command without any pre-configured scope." + }, + { + "description": "Denies the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-position", + "markdownDescription": "Denies the inner_position command without any pre-configured scope." + }, + { + "description": "Denies the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-size", + "markdownDescription": "Denies the inner_size command without any pre-configured scope." + }, + { + "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-internal-toggle-maximize", + "markdownDescription": "Denies the internal_toggle_maximize command without any pre-configured scope." + }, + { + "description": "Denies the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-always-on-top", + "markdownDescription": "Denies the is_always_on_top command without any pre-configured scope." + }, + { + "description": "Denies the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-closable", + "markdownDescription": "Denies the is_closable command without any pre-configured scope." + }, + { + "description": "Denies the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-decorated", + "markdownDescription": "Denies the is_decorated command without any pre-configured scope." + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." + }, + { + "description": "Denies the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-focused", + "markdownDescription": "Denies the is_focused command without any pre-configured scope." + }, + { + "description": "Denies the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-fullscreen", + "markdownDescription": "Denies the is_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximizable", + "markdownDescription": "Denies the is_maximizable command without any pre-configured scope." + }, + { + "description": "Denies the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximized", + "markdownDescription": "Denies the is_maximized command without any pre-configured scope." + }, + { + "description": "Denies the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimizable", + "markdownDescription": "Denies the is_minimizable command without any pre-configured scope." + }, + { + "description": "Denies the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimized", + "markdownDescription": "Denies the is_minimized command without any pre-configured scope." + }, + { + "description": "Denies the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-resizable", + "markdownDescription": "Denies the is_resizable command without any pre-configured scope." + }, + { + "description": "Denies the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-visible", + "markdownDescription": "Denies the is_visible command without any pre-configured scope." + }, + { + "description": "Denies the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-maximize", + "markdownDescription": "Denies the maximize command without any pre-configured scope." + }, + { + "description": "Denies the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-minimize", + "markdownDescription": "Denies the minimize command without any pre-configured scope." + }, + { + "description": "Denies the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-monitor-from-point", + "markdownDescription": "Denies the monitor_from_point command without any pre-configured scope." + }, + { + "description": "Denies the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-position", + "markdownDescription": "Denies the outer_position command without any pre-configured scope." + }, + { + "description": "Denies the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-size", + "markdownDescription": "Denies the outer_size command without any pre-configured scope." + }, + { + "description": "Denies the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-primary-monitor", + "markdownDescription": "Denies the primary_monitor command without any pre-configured scope." + }, + { + "description": "Denies the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-request-user-attention", + "markdownDescription": "Denies the request_user_attention command without any pre-configured scope." + }, + { + "description": "Denies the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-scale-factor", + "markdownDescription": "Denies the scale_factor command without any pre-configured scope." + }, + { + "description": "Denies the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-bottom", + "markdownDescription": "Denies the set_always_on_bottom command without any pre-configured scope." + }, + { + "description": "Denies the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-top", + "markdownDescription": "Denies the set_always_on_top command without any pre-configured scope." + }, + { + "description": "Denies the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-background-color", + "markdownDescription": "Denies the set_background_color command without any pre-configured scope." + }, + { + "description": "Denies the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-count", + "markdownDescription": "Denies the set_badge_count command without any pre-configured scope." + }, + { + "description": "Denies the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-label", + "markdownDescription": "Denies the set_badge_label command without any pre-configured scope." + }, + { + "description": "Denies the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-closable", + "markdownDescription": "Denies the set_closable command without any pre-configured scope." + }, + { + "description": "Denies the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-content-protected", + "markdownDescription": "Denies the set_content_protected command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-grab", + "markdownDescription": "Denies the set_cursor_grab command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-icon", + "markdownDescription": "Denies the set_cursor_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-position", + "markdownDescription": "Denies the set_cursor_position command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-visible", + "markdownDescription": "Denies the set_cursor_visible command without any pre-configured scope." + }, + { + "description": "Denies the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-decorations", + "markdownDescription": "Denies the set_decorations command without any pre-configured scope." + }, + { + "description": "Denies the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-effects", + "markdownDescription": "Denies the set_effects command without any pre-configured scope." + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." + }, + { + "description": "Denies the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focus", + "markdownDescription": "Denies the set_focus command without any pre-configured scope." + }, + { + "description": "Denies the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focusable", + "markdownDescription": "Denies the set_focusable command without any pre-configured scope." + }, + { + "description": "Denies the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-fullscreen", + "markdownDescription": "Denies the set_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-ignore-cursor-events", + "markdownDescription": "Denies the set_ignore_cursor_events command without any pre-configured scope." + }, + { + "description": "Denies the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-max-size", + "markdownDescription": "Denies the set_max_size command without any pre-configured scope." + }, + { + "description": "Denies the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-maximizable", + "markdownDescription": "Denies the set_maximizable command without any pre-configured scope." + }, + { + "description": "Denies the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-min-size", + "markdownDescription": "Denies the set_min_size command without any pre-configured scope." + }, + { + "description": "Denies the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-minimizable", + "markdownDescription": "Denies the set_minimizable command without any pre-configured scope." + }, + { + "description": "Denies the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-overlay-icon", + "markdownDescription": "Denies the set_overlay_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-position", + "markdownDescription": "Denies the set_position command without any pre-configured scope." + }, + { + "description": "Denies the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-progress-bar", + "markdownDescription": "Denies the set_progress_bar command without any pre-configured scope." + }, + { + "description": "Denies the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-resizable", + "markdownDescription": "Denies the set_resizable command without any pre-configured scope." + }, + { + "description": "Denies the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-shadow", + "markdownDescription": "Denies the set_shadow command without any pre-configured scope." + }, + { + "description": "Denies the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-simple-fullscreen", + "markdownDescription": "Denies the set_simple_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size", + "markdownDescription": "Denies the set_size command without any pre-configured scope." + }, + { + "description": "Denies the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size-constraints", + "markdownDescription": "Denies the set_size_constraints command without any pre-configured scope." + }, + { + "description": "Denies the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-skip-taskbar", + "markdownDescription": "Denies the set_skip_taskbar command without any pre-configured scope." + }, + { + "description": "Denies the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-theme", + "markdownDescription": "Denies the set_theme command without any pre-configured scope." + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." + }, + { + "description": "Denies the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title-bar-style", + "markdownDescription": "Denies the set_title_bar_style command without any pre-configured scope." + }, + { + "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-visible-on-all-workspaces", + "markdownDescription": "Denies the set_visible_on_all_workspaces command without any pre-configured scope." + }, + { + "description": "Denies the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-show", + "markdownDescription": "Denies the show command without any pre-configured scope." + }, + { + "description": "Denies the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-dragging", + "markdownDescription": "Denies the start_dragging command without any pre-configured scope." + }, + { + "description": "Denies the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-resize-dragging", + "markdownDescription": "Denies the start_resize_dragging command without any pre-configured scope." + }, + { + "description": "Denies the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-theme", + "markdownDescription": "Denies the theme command without any pre-configured scope." + }, + { + "description": "Denies the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-title", + "markdownDescription": "Denies the title command without any pre-configured scope." + }, + { + "description": "Denies the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-toggle-maximize", + "markdownDescription": "Denies the toggle_maximize command without any pre-configured scope." + }, + { + "description": "Denies the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unmaximize", + "markdownDescription": "Denies the unmaximize command without any pre-configured scope." + }, + { + "description": "Denies the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unminimize", + "markdownDescription": "Denies the unminimize command without any pre-configured scope." + }, + { + "description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-message`\n- `allow-save`\n- `allow-open`", + "type": "string", + "const": "dialog:default", + "markdownDescription": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-message`\n- `allow-save`\n- `allow-open`" + }, + { + "description": "Enables the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)", + "type": "string", + "const": "dialog:allow-ask", + "markdownDescription": "Enables the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)" + }, + { + "description": "Enables the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)", + "type": "string", + "const": "dialog:allow-confirm", + "markdownDescription": "Enables the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)" + }, + { + "description": "Enables the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-message", + "markdownDescription": "Enables the message command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-save", + "markdownDescription": "Enables the save command without any pre-configured scope." + }, + { + "description": "Denies the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)", + "type": "string", + "const": "dialog:deny-ask", + "markdownDescription": "Denies the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)" + }, + { + "description": "Denies the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)", + "type": "string", + "const": "dialog:deny-confirm", + "markdownDescription": "Denies the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)" + }, + { + "description": "Denies the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-message", + "markdownDescription": "Denies the message command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-save", + "markdownDescription": "Denies the save command without any pre-configured scope." + }, + { + "description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`", + "type": "string", + "const": "updater:default", + "markdownDescription": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`" + }, + { + "description": "Enables the check command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-check", + "markdownDescription": "Enables the check command without any pre-configured scope." + }, + { + "description": "Enables the download command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-download", + "markdownDescription": "Enables the download command without any pre-configured scope." + }, + { + "description": "Enables the download_and_install command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-download-and-install", + "markdownDescription": "Enables the download_and_install command without any pre-configured scope." + }, + { + "description": "Enables the install command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-install", + "markdownDescription": "Enables the install command without any pre-configured scope." + }, + { + "description": "Denies the check command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-check", + "markdownDescription": "Denies the check command without any pre-configured scope." + }, + { + "description": "Denies the download command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-download", + "markdownDescription": "Denies the download command without any pre-configured scope." + }, + { + "description": "Denies the download_and_install command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-download-and-install", + "markdownDescription": "Denies the download_and_install command without any pre-configured scope." + }, + { + "description": "Denies the install command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-install", + "markdownDescription": "Denies the install command without any pre-configured scope." + } + ] + }, + "Value": { + "description": "All supported ACL values.", + "anyOf": [ + { + "description": "Represents a null JSON value.", + "type": "null" + }, + { + "description": "Represents a [`bool`].", + "type": "boolean" + }, + { + "description": "Represents a valid ACL [`Number`].", + "allOf": [ + { + "$ref": "#/definitions/Number" + } + ] + }, + { + "description": "Represents a [`String`].", + "type": "string" + }, + { + "description": "Represents a list of other [`Value`]s.", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + { + "description": "Represents a map of [`String`] keys to [`Value`]s.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Value" + } + } + ] + }, + "Number": { + "description": "A valid ACL number.", + "anyOf": [ + { + "description": "Represents an [`i64`].", + "type": "integer", + "format": "int64" + }, + { + "description": "Represents a [`f64`].", + "type": "number", + "format": "double" + } + ] + }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + } + } +} \ No newline at end of file diff --git a/src-tauri/src/context.rs b/src-tauri/src/context.rs index 5bbcc360..ed8bf5ea 100644 --- a/src-tauri/src/context.rs +++ b/src-tauri/src/context.rs @@ -230,6 +230,8 @@ pub(super) fn team_agent_system_prompt_for_estimate( prompt: String::new(), model: model.clone(), enabled: true, + source: None, + source_path: None, }; let base = subagent_system_prompt(base, &config_agent); format!( diff --git a/src-tauri/src/git.rs b/src-tauri/src/git.rs index ff77f155..fc2d814a 100644 --- a/src-tauri/src/git.rs +++ b/src-tauri/src/git.rs @@ -4,9 +4,31 @@ use std::ffi::OsStr; use std::ffi::OsString; use std::path::Component; use std::process::Stdio; +use std::sync::OnceLock; #[cfg(test)] use std::sync::atomic::{AtomicUsize, Ordering}; +#[cfg(windows)] +use std::os::windows::process::CommandExt; + +#[cfg(windows)] +const CREATE_NO_WINDOW: u32 = 0x0800_0000; + +static RESOLVED_GIT: OnceLock> = OnceLock::new(); +static RESOLVED_GH: OnceLock> = OnceLock::new(); + +/// On Windows, console-subsystem binaries (`git.exe`, `gh.exe`, …) spawn a +/// visible console unless we pass `CREATE_NO_WINDOW`. The Git panel probes +/// PATH heavily on each refresh; without this flag, clicking the tab can +/// flash dozens of console windows and re-trigger focus handlers in a loop. +#[cfg(windows)] +fn hide_subprocess_console(command: &mut Command) { + command.creation_flags(CREATE_NO_WINDOW); +} + +#[cfg(not(windows))] +fn hide_subprocess_console(_command: &mut Command) {} + #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub(super) struct GitRepositorySnapshot { @@ -669,6 +691,20 @@ fn command_available(program: &str) -> bool { } fn resolve_executable(program: &str) -> Option { + if program.eq_ignore_ascii_case("git") { + return RESOLVED_GIT + .get_or_init(|| resolve_executable_uncached("git")) + .clone(); + } + if program.eq_ignore_ascii_case("gh") { + return RESOLVED_GH + .get_or_init(|| resolve_executable_uncached("gh")) + .clone(); + } + resolve_executable_uncached(program) +} + +fn resolve_executable_uncached(program: &str) -> Option { let direct = PathBuf::from(program); if direct.components().count() > 1 { if let Some(candidate) = find_working_executable(&direct) { @@ -810,11 +846,14 @@ fn executable_works(path: &Path) -> bool { if !path.exists() { return false; } - Command::new(path) + let mut command = Command::new(path); + command .arg("--version") .stdin(Stdio::null()) .stdout(Stdio::null()) - .stderr(Stdio::null()) + .stderr(Stdio::null()); + hide_subprocess_console(&mut command); + command .status() .map(|status| status.success()) .unwrap_or(false) @@ -1384,6 +1423,7 @@ fn run_output_with_program( command.arg(OsStr::new(arg)); } command.stdin(Stdio::null()); + hide_subprocess_console(&mut command); let output = command .output() .with_context(|| format!("unable to launch {program_label}"))?; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index f50de696..ec539b3c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -36,6 +36,8 @@ use sinew_anthropic::{ use sinew_app::{ checkpoint_from_snapshots, clean_context_descriptor, compact_conversation_history, copy_workspace_entries, create_installed_skill, create_workspace_directory, + import_skills_from_provider, import_sub_agents_from_provider, ImportSkillsResult, + ImportSubAgentsResult, create_workspace_file, delete_workspace_entry, import_workspace_paths, list_installed_skills, list_workspace_entries, list_workspace_files, normalize_workspace_root, probe_mcp_servers, read_external_file, read_workspace_file, rename_workspace_entry, resolve_terminal_path, @@ -273,6 +275,8 @@ pub fn run() { workspace::resolve_terminal_path_command, workspace::read_external_file_command, workspace::delete_skill_command, + workspace::import_skills_command, + workspace::import_sub_agents_command, workspace::create_skill_command, workspace::update_skill_content_command, workspace::open_external_url_command, diff --git a/src-tauri/src/platform.rs b/src-tauri/src/platform.rs index 906094f1..27bc9858 100644 --- a/src-tauri/src/platform.rs +++ b/src-tauri/src/platform.rs @@ -1,5 +1,6 @@ use crate::*; +#[cfg(not(target_os = "windows"))] use tauri::{ menu::{AboutMetadata, Menu, MenuItemBuilder, PredefinedMenuItem, Submenu, SubmenuBuilder}, Runtime, @@ -289,16 +290,29 @@ pub(super) fn emit_workspace_file_change( ); } +/// Strip Win32 `\\?\` / `\\?\UNC\` prefixes so shell tools receive normal paths. +fn path_for_shell(path: &Path) -> PathBuf { + let raw = path.to_string_lossy(); + if let Some(rest) = raw.strip_prefix(r"\\?\UNC\") { + return PathBuf::from(format!(r"\\{rest}")); + } + if let Some(rest) = raw.strip_prefix(r"\\?\") { + return PathBuf::from(rest); + } + path.to_path_buf() +} + pub(super) fn reveal_path(path: &Path) -> Result<()> { if !path.exists() { anyhow::bail!("path does not exist"); } + let shell_path = path_for_shell(path); #[cfg(target_os = "macos")] { let status = Command::new("open") .arg("-R") - .arg(path) + .arg(&shell_path) .status() .context("unable to reveal item in Finder")?; if !status.success() { @@ -309,22 +323,20 @@ pub(super) fn reveal_path(path: &Path) -> Result<()> { #[cfg(target_os = "windows")] { - let status = Command::new("explorer") - .arg(format!("/select,{}", path.display())) - .status() + // explorer.exe often returns exit code 1 even when reveal succeeds. + Command::new("explorer") + .arg(format!("/select,{}", shell_path.display())) + .spawn() .context("unable to reveal item in Explorer")?; - if !status.success() { - anyhow::bail!("Explorer reveal failed"); - } return Ok(()); } #[cfg(all(unix, not(target_os = "macos")))] { - let target = if path.is_dir() { - path + let target = if shell_path.is_dir() { + &shell_path } else { - path.parent().unwrap_or(path) + shell_path.parent().unwrap_or(&shell_path) }; let status = Command::new("xdg-open") .arg(target) diff --git a/src-tauri/src/workspace.rs b/src-tauri/src/workspace.rs index f116ac18..a3f94316 100644 --- a/src-tauri/src/workspace.rs +++ b/src-tauri/src/workspace.rs @@ -414,6 +414,68 @@ pub(super) struct CreateSkillOutput { pub(super) skills: Vec, } +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(super) struct ImportSkillsCommandInput { + pub(super) workspace_path: String, + pub(super) provider: String, +} + +#[tauri::command] +pub(super) async fn import_skills_command( + app: AppHandle, + input: ImportSkillsCommandInput, +) -> std::result::Result { + let workspace_root = + normalize_workspace_root(&input.workspace_path).map_err(error_to_string)?; + let result = + import_skills_from_provider(&workspace_root, &input.provider).map_err(error_to_string)?; + for name in &result.imported { + let relative = format!(".agents/skills/{name}/SKILL.md"); + emit_workspace_file_change(&app, &workspace_root, &relative); + } + Ok(result) +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(super) struct ImportSubAgentsCommandInput { + pub(super) workspace_path: String, + pub(super) provider: String, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub(super) struct ImportSubAgentsOutput { + pub(super) settings: SubAgentSettings, + pub(super) result: ImportSubAgentsResult, +} + +#[tauri::command] +pub(super) async fn import_sub_agents_command( + state: State<'_, DesktopState>, + input: ImportSubAgentsCommandInput, +) -> std::result::Result { + let workspace_root = + normalize_workspace_root(&input.workspace_path).map_err(error_to_string)?; + let current = state.store.load_sub_agent_settings().map_err(error_to_string)?; + let (merged, result) = import_sub_agents_from_provider( + &workspace_root, + &input.provider, + ¤t, + &state.default_model, + ) + .map_err(error_to_string)?; + let saved = state + .store + .save_sub_agent_settings(&merged) + .map_err(error_to_string)?; + Ok(ImportSubAgentsOutput { + settings: saved, + result, + }) +} + #[tauri::command] pub(super) async fn create_skill_command( state: State<'_, DesktopState>, diff --git a/src/components/EditorPane.tsx b/src/components/EditorPane.tsx index 453e534d..a540b461 100644 --- a/src/components/EditorPane.tsx +++ b/src/components/EditorPane.tsx @@ -9,6 +9,7 @@ import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker"; import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker"; import { useCallback, useEffect, useRef, useState, type ReactNode } from "react"; import { Icon } from "@iconify/react"; +import { dropSlotToMoveIndex } from "../lib/editorTabs"; import { languageForPath } from "../lib/language"; import { fileIcon } from "../lib/fileIcon"; import { Markdown } from "./chat/Markdown"; @@ -45,6 +46,7 @@ type Props = { tabs: EditorTab[]; activeIndex: number; onActivate: (index: number) => void; + onReorderTabs: (fromIndex: number, toIndex: number) => void; onClose: (index: number) => void; onChange: (index: number, value: string) => void; onSave: (index: number) => void; @@ -61,6 +63,7 @@ export function EditorPane({ tabs, activeIndex, onActivate, + onReorderTabs, onClose, onChange, onSave, @@ -89,6 +92,17 @@ export function EditorPane({ const [imageMenu, setImageMenu] = useState<{ x: number; y: number } | null>( null, ); + const tabsStripRef = useRef(null); + const [tabReorderFrom, setTabReorderFrom] = useState(null); + const [tabDropSlot, setTabDropSlot] = useState(null); + const tabPointerRef = useRef<{ + index: number; + pointerId: number; + startX: number; + dragging: boolean; + } | null>(null); + + const TAB_DRAG_THRESHOLD_PX = 6; const activeTab: EditorTab | undefined = settingsActive ? undefined : tabs[activeIndex]; // Close the image context menu whenever the user switches tabs or // toggles into the settings view, so it never lingers on the wrong file. @@ -310,15 +324,119 @@ export function EditorPane({ [activeTab, activeIndex, onChange], ); + const clearTabReorder = useCallback(() => { + tabPointerRef.current = null; + setTabReorderFrom(null); + setTabDropSlot(null); + }, []); + + // Pointer-driven reorder: HTML5 DnD is unreliable in Tauri/WebView2 because + // custom dataTransfer types are hidden until drop, so dragover never accepts. + const resolveDropSlotFromPoint = useCallback((clientX: number) => { + const strip = tabsStripRef.current; + if (!strip) return null; + const tabEls = strip.querySelectorAll('[data-editor-tab="true"]'); + if (tabEls.length === 0) return 0; + for (let index = 0; index < tabEls.length; index += 1) { + const rect = tabEls[index].getBoundingClientRect(); + if (clientX < rect.left) return index; + if (clientX <= rect.right) { + return clientX < rect.left + rect.width / 2 ? index : index + 1; + } + } + return tabEls.length; + }, []); + + const handleTabPointerDown = useCallback( + (event: React.PointerEvent, index: number) => { + if (event.button !== 0) return; + if ((event.target as HTMLElement).closest(".tab__close")) return; + tabPointerRef.current = { + index, + pointerId: event.pointerId, + startX: event.clientX, + dragging: false, + }; + setTabDropSlot(null); + }, + [], + ); + + const handleTabsPointerMove = useCallback( + (event: React.PointerEvent) => { + const pointer = tabPointerRef.current; + if (!pointer || pointer.pointerId !== event.pointerId) return; + + if (!pointer.dragging) { + if (Math.abs(event.clientX - pointer.startX) < TAB_DRAG_THRESHOLD_PX) { + return; + } + pointer.dragging = true; + setTabReorderFrom(pointer.index); + tabsStripRef.current?.setPointerCapture(event.pointerId); + } + + const slot = resolveDropSlotFromPoint(event.clientX); + if (slot !== null) setTabDropSlot(slot); + }, + [resolveDropSlotFromPoint], + ); + + const finishTabPointer = useCallback( + (event: React.PointerEvent) => { + const pointer = tabPointerRef.current; + if (!pointer || pointer.pointerId !== event.pointerId) return; + + if (event.currentTarget.hasPointerCapture(event.pointerId)) { + event.currentTarget.releasePointerCapture(event.pointerId); + } + + if (!pointer.dragging) { + onActivate(pointer.index); + clearTabReorder(); + return; + } + + const from = tabReorderFrom ?? pointer.index; + const slot = + tabDropSlot ?? resolveDropSlotFromPoint(event.clientX) ?? from; + const to = dropSlotToMoveIndex(from, slot, tabs.length); + if (from !== to) { + onReorderTabs(from, to); + } + clearTabReorder(); + }, + [ + clearTabReorder, + onActivate, + onReorderTabs, + resolveDropSlotFromPoint, + tabDropSlot, + tabReorderFrom, + tabs.length, + ], + ); + return (
-
+
{tabs.map((tab, index) => (
onActivate(index)} + data-dragging={tabReorderFrom === index ? "true" : "false"} + data-drop-before={tabDropSlot === index ? "true" : "false"} + data-drop-after={tabDropSlot === index + 1 ? "true" : "false"} + onPointerDown={(event) => handleTabPointerDown(event, index)} title={tab.relativePath} > diff --git a/src/components/SettingsPane.tsx b/src/components/SettingsPane.tsx index dcf6e15d..bada0b43 100644 --- a/src/components/SettingsPane.tsx +++ b/src/components/SettingsPane.tsx @@ -42,6 +42,54 @@ import type { WebSearchProvider, } from "../types"; +function settingsStatusTone(status: string): "ok" | "error" { + const lower = status.toLowerCase(); + if ( + status === "Saved" || + status === "Created" || + status === "Deleted" || + lower.startsWith("imported") || + lower.startsWith("nothing imported") || + lower.includes("already present") || + lower.includes("no skills found") + ) { + return "ok"; + } + return "error"; +} + +function formatSkillDisplayPath(skill: InstalledSkill): string { + const normalized = skill.absolutePath.replace(/\\/g, "/"); + const parts = normalized.split("/").filter(Boolean); + const folder = parts.at(-2) ?? skill.name; + return `${skill.rootLabel}/${folder}/SKILL.md`; +} + +function formatSubAgentDisplayPath(agent: SubAgentConfig): string { + if (!agent.sourcePath) return "Configured in Sinew"; + const normalized = agent.sourcePath.replace(/\\/g, "/"); + const parts = normalized.split("/").filter(Boolean); + const file = parts.at(-1) ?? "agent.md"; + const agentsIdx = parts.lastIndexOf("agents"); + if (agentsIdx >= 0) { + const prefix = + agent.source === "workspace" + ? ".claude/agents" + : "~/.claude/agents"; + const rel = parts.slice(agentsIdx + 1, -1); + return rel.length > 0 ? `${prefix}/${rel.join("/")}/${file}` : `${prefix}/${file}`; + } + return normalized; +} + +function revealInFileManagerLabel(): string { + const platform = + typeof navigator !== "undefined" ? navigator.platform.toLowerCase() : ""; + if (platform.includes("mac")) return "Reveal in Finder"; + if (platform.includes("win")) return "Show in Explorer"; + return "Show in File Manager"; +} + const EMPTY_SETTINGS: McpSettings = { servers: [] }; const FALLBACK_TOOL_SETTINGS: ToolSettings = { tools: [], @@ -98,7 +146,9 @@ export function SettingsPane({ workspacePath }: Props) { const [subAgentsLoading, setSubAgentsLoading] = useState(false); const [subAgentsSaving, setSubAgentsSaving] = useState(false); const [subAgentsStatus, setSubAgentsStatus] = useState(null); + const [subAgentsError, setSubAgentsError] = useState(null); const [selectedSubAgentId, setSelectedSubAgentId] = useState(null); + const [subAgentFilter, setSubAgentFilter] = useState(""); const [toolSettings, setToolSettings] = useState(null); const [savedToolSettingsJson, setSavedToolSettingsJson] = useState(""); @@ -922,6 +972,56 @@ export function SettingsPane({ workspacePath }: Props) { [skills, skillsDirty, workspacePath], ); + const importSkills = useCallback( + async (provider: "claude" | "codex") => { + setSkillsLoading(true); + setSkillsError(null); + setSkillsStatus(null); + try { + const result = await api.importSkills(workspacePath, provider); + const list = await api.listInstalledSkills(workspacePath); + setSkills(list); + setSavedSkillsJson(skillsFingerprint(list)); + if (result.imported.length > 0) { + setSelectedSkillName((current) => { + if (current && list.some((item) => item.name === current)) { + return current; + } + const folder = result.imported[0]; + const match = list.find( + (item) => + item.name === folder || + item.absolutePath.includes(`/.agents/skills/${folder}/`), + ); + return match?.name ?? list[0]?.name ?? null; + }); + } + if (result.imported.length === 0 && result.skipped.length === 0) { + setSkillsStatus("No skills found at source paths"); + } else if (result.imported.length === 0) { + setSkillsStatus( + `Nothing imported (${result.skipped.length} already present)`, + ); + } else { + const suffix = + result.skipped.length > 0 + ? ` · ${result.skipped.length} skipped` + : ""; + setSkillsStatus( + `Imported ${result.imported.length} skill${result.imported.length === 1 ? "" : "s"}${suffix}`, + ); + } + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + setSkillsError(message); + setSkillsStatus(message); + } finally { + setSkillsLoading(false); + } + }, + [workspacePath], + ); + const createSkill = useCallback(async () => { setSkillsSaving(true); setSkillsError(null); @@ -984,6 +1084,18 @@ export function SettingsPane({ workspacePath }: Props) { [skills, workspacePath], ); + const filteredSubAgents = useMemo(() => { + const needle = subAgentFilter.trim().toLowerCase(); + if (!needle) return subAgentSettings.agents; + return subAgentSettings.agents.filter((agent) => { + if (agent.name.toLowerCase().includes(needle)) return true; + if (agent.description.toLowerCase().includes(needle)) return true; + if (agent.id.toLowerCase().includes(needle)) return true; + if (agent.prompt.toLowerCase().includes(needle)) return true; + return false; + }); + }, [subAgentFilter, subAgentSettings.agents]); + const selectedSubAgent = subAgentSettings.agents.find((agent) => agent.id === selectedSubAgentId) ?? null; @@ -1034,6 +1146,7 @@ export function SettingsPane({ workspacePath }: Props) { }, [availableModels, subAgentSettings.agents.length]); const deleteSubAgent = useCallback((id: string) => { + setSubAgentsStatus(null); setSubAgentSettings((current) => { const agents = current.agents.filter((agent) => agent.id !== id); setSelectedSubAgentId((selected) => { @@ -1042,6 +1155,64 @@ export function SettingsPane({ workspacePath }: Props) { }); return { agents }; }); + setSubAgentsStatus("Deleted"); + }, []); + + const importSubAgents = useCallback(async () => { + setSubAgentsLoading(true); + setSubAgentsError(null); + setSubAgentsStatus(null); + try { + const output = await api.importSubAgents(workspacePath, "claude"); + const saved = normalizeSubAgentSettings(output.settings); + setSubAgentSettings(saved); + setSavedSubAgentJson(subAgentSettingsFingerprint(saved)); + if (output.result.imported.length > 0) { + setSelectedSubAgentId((current) => { + if (current && saved.agents.some((agent) => agent.id === current)) { + return current; + } + const first = output.result.imported[0]; + const match = saved.agents.find( + (agent) => agent.name === first || agent.id === first, + ); + return match?.id ?? saved.agents[0]?.id ?? null; + }); + } + if (output.result.imported.length === 0 && output.result.skipped.length === 0) { + setSubAgentsStatus("No sub-agents found at source paths"); + } else if (output.result.imported.length === 0) { + setSubAgentsStatus( + `Nothing imported (${output.result.skipped.length} already present)`, + ); + } else { + const suffix = + output.result.skipped.length > 0 + ? ` · ${output.result.skipped.length} skipped` + : ""; + setSubAgentsStatus( + `Imported ${output.result.imported.length} agent${output.result.imported.length === 1 ? "" : "s"}${suffix}`, + ); + } + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + setSubAgentsError(message); + setSubAgentsStatus(message); + } finally { + setSubAgentsLoading(false); + } + }, [workspacePath]); + + const revealSubAgent = useCallback(async (agent: SubAgentConfig) => { + if (!agent.sourcePath) return; + setSubAgentsError(null); + try { + await api.revealAbsolutePath(agent.sourcePath); + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + setSubAgentsError(message); + setSubAgentsStatus(message); + } }, []); const handleEditorMount: OnMount = useCallback((editor, monaco) => { @@ -1251,6 +1422,7 @@ export function SettingsPane({ workspacePath }: Props) { onRefresh={() => void loadSkills()} onSave={() => void saveSkills()} onCreate={() => void createSkill()} + onImport={(provider) => void importSkills(provider)} onToggleSkill={toggleSkillEnabled} onRevealSkill={(skill) => void revealSkill(skill)} onDeleteSkill={(skill) => void deleteSkill(skill)} @@ -1258,18 +1430,25 @@ export function SettingsPane({ workspacePath }: Props) { /> ) : ( void saveSubAgents()} onUpdate={updateSubAgent} + onImport={() => void importSubAgents()} + onRescan={() => void importSubAgents()} + onReveal={(agent) => void revealSubAgent(agent)} /> )} @@ -2704,36 +2883,133 @@ function ServerDetail({ server, probe, probing, knownToolCount }: ServerDetailPr // ---- Sub-agent section ------------------------------------------------- +const SUB_AGENT_IMPORT_SOURCES = [ + { + id: "claude" as const, + label: "Claude Code", + hint: "~/.claude/agents · .claude/agents", + icon: PROVIDERS.find((p) => p.value === "anthropic")?.icon ?? "simple-icons:anthropic", + }, +]; + +function SubAgentImportPicker({ + disabled, + onImport, +}: { + disabled: boolean; + onImport: () => void; +}) { + const [open, setOpen] = useState(false); + const ref = useRef(null); + + useEffect(() => { + if (!open) return; + const onDown = (event: MouseEvent) => { + if ( + ref.current && + event.target instanceof Node && + !ref.current.contains(event.target) + ) { + setOpen(false); + } + }; + document.addEventListener("mousedown", onDown); + return () => document.removeEventListener("mousedown", onDown); + }, [open]); + + return ( +
+ + {open && !disabled && ( +
+ {SUB_AGENT_IMPORT_SOURCES.map((source) => ( + + ))} +
+ )} +
+ ); +} + type SubAgentsSectionProps = { - settings: SubAgentSettings; + agents: SubAgentConfig[]; + allAgents: SubAgentConfig[]; selectedAgent: SubAgentConfig | null; loading: boolean; saving: boolean; dirty: boolean; + error: string | null; status: string | null; + filter: string; + onFilterChange: (value: string) => void; availableModels: readonly ModelEntry[]; onSelect: (id: string) => void; onAdd: () => void; onDelete: (id: string) => void; onSave: () => void; onUpdate: (id: string, patch: Partial) => void; + onImport: () => void; + onRescan: () => void; + onReveal: (agent: SubAgentConfig) => void; }; function SubAgentsSection({ - settings, + agents, + allAgents, selectedAgent, loading, saving, dirty, + error, status, + filter, + onFilterChange, availableModels, onSelect, onAdd, onDelete, onSave, onUpdate, + onImport, + onRescan, + onReveal, }: SubAgentsSectionProps) { - const enabledCount = settings.agents.filter((agent) => agent.enabled).length; + const total = allAgents.length; + const visible = agents.length; + const enabled = allAgents.filter((agent) => agent.enabled).length; return ( <> @@ -2741,24 +3017,42 @@ function SubAgentsSection({

Sub-agents

- {settings.agents.length === 0 - ? "Create focused agents the main agent can call as tools." - : `${enabledCount}/${settings.agents.length} available to the main agent`} + {loading + ? "Loading…" + : total === 0 + ? "Add agents or import from .claude/agents." + : `${enabled}/${total} available to the main agent`}

{status && ( {status} )} + + + + -
+
{selectedAgent ? ( - onUpdate(selectedAgent.id, patch)} onDelete={() => onDelete(selectedAgent.id)} + onReveal={() => onReveal(selectedAgent)} /> ) : (
- + - Select or create an agent + {total === 0 ? "Nothing to preview" : "Select an agent"}
)} -
+
); @@ -2934,16 +3277,20 @@ function settingsThinkingLabel( return level.label; } -function SubAgentEditor({ +function SubAgentPreview({ agent, + saving, availableModels, onUpdate, onDelete, + onReveal, }: { agent: SubAgentConfig; + saving: boolean; availableModels: readonly ModelEntry[]; onUpdate: (patch: Partial) => void; onDelete: () => void; + onReveal: () => void; }) { const rawModelId = modelIdFromRef(agent.model); const thinking = thinkingFromRef(agent.model); @@ -2977,6 +3324,7 @@ function SubAgentEditor({ }; const [confirmDelete, setConfirmDelete] = useState(false); + const [editingPrompt, setEditingPrompt] = useState(false); useEffect(() => { if (!confirmDelete) return; const id = window.setTimeout(() => setConfirmDelete(false), 3000); @@ -2984,63 +3332,84 @@ function SubAgentEditor({ }, [confirmDelete]); useEffect(() => { setConfirmDelete(false); + setEditingPrompt(false); }, [agent.id]); return ( -
-
- onUpdate({ name: event.target.value })} - placeholder="Untitled agent" - aria-label="Agent name" - /> -
- - +
+
+ + {agent.sourcePath && ( + + )} + +
-
- -
-