diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d90e382..a4eea12 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -88,26 +88,3 @@ jobs: files: | install.sh install.ps1 - - build-windows: - name: Build devcontainer-mcp-windows-x64 - runs-on: windows-latest - steps: - - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - with: - targets: x86_64-pc-windows-msvc - - uses: Swatinem/rust-cache@v2 - with: - key: x86_64-pc-windows-msvc - - - name: Build - run: cargo build --release --target x86_64-pc-windows-msvc -p devcontainer-mcp - - - name: Package binary - run: Compress-Archive -Path target/x86_64-pc-windows-msvc/release/devcontainer-mcp.exe -DestinationPath devcontainer-mcp-windows-x64.zip - - - name: Upload release asset - uses: softprops/action-gh-release@v3 - with: - files: devcontainer-mcp-windows-x64.zip diff --git a/SKILL.md b/SKILL.md index a9f8937..367de62 100644 --- a/SKILL.md +++ b/SKILL.md @@ -47,15 +47,6 @@ tools: - devpod_file_write - devpod_file_edit - devpod_file_list - - wsl_list - - wsl_exec - - wsl_terminate - - wsl_shutdown - - wsl_set_default - - wsl_file_read - - wsl_file_write - - wsl_file_edit - - wsl_file_list --- # DevContainer MCP Skill @@ -69,7 +60,7 @@ You have access to `devcontainer-mcp`, an MCP server that manages dev container **If a project has `.devcontainer/devcontainer.json`, ALL work MUST happen inside a dev container — never install dependencies, run builds, or execute code directly on the host.** -**Use ONLY the MCP tools listed here.** Do not invoke `docker`, `devcontainer`, `devpod`, `gh`, or `wsl` CLI commands directly — the MCP tools wrap these CLIs with proper error handling, auth resolution, and escaping. Direct CLI usage bypasses these safeguards. This applies even when the user asks to work "directly in WSL" or "not in a devcontainer" — use `wsl_exec` and WSL file tools instead of raw `wsl` commands. +**Use ONLY the MCP tools listed here.** Do not invoke `docker`, `devcontainer`, `devpod`, or `gh` CLI commands directly — the MCP tools wrap these CLIs with proper error handling, auth resolution, and escaping. Direct CLI usage bypasses these safeguards. ## Authentication @@ -175,51 +166,6 @@ codespaces_ssh(auth: "github-USERNAME", codespace: "codespace-name", command: "n codespaces_stop(auth: "github-USERNAME", codespace: "codespace-name") ``` -## Workflow: WSL (Windows only) - -> **Use these tools — not raw `wsl.exe` or PowerShell `wsl` commands.** When a user asks to work "in WSL" or "directly in WSL", use `wsl_exec` and the WSL file tools below — these ARE the way to work in WSL through MCP. - -WSL tools let you clone repos, build projects, and run commands inside any WSL distribution — without devcontainers or Docker. - -### 1. List available distributions -``` -wsl_list() -``` - -### 2. Clone and build a repo in WSL -``` -wsl_exec(distro: "Ubuntu", command: "git clone https://github.com/org/repo.git /home/user/repo") -wsl_exec(distro: "Ubuntu", command: "cd /home/user/repo && cargo build") -``` - -### 3. Execute any command inside a distribution -``` -wsl_exec(distro: "Ubuntu", command: "apt list --installed") -``` - -### 4. Set the default distribution -``` -wsl_set_default(distro: "Ubuntu") -``` - -### 5. Stop a distribution -``` -wsl_terminate(distro: "Ubuntu") -``` - -### 6. Shut down all WSL distributions -``` -wsl_shutdown() -``` - -### File operations in WSL -``` -wsl_file_read(distro: "Ubuntu", path: "/home/user/project/src/main.rs") -wsl_file_write(distro: "Ubuntu", path: "/home/user/file.txt", content: "fn main() {}") -wsl_file_edit(distro: "Ubuntu", path: "/home/user/file.txt", old_str: "fn main", new_str: "fn start") -wsl_file_list(distro: "Ubuntu", path: "/home/user/project") -``` - ## Self-Healing If `devpod_up`, `devcontainer_up`, or `codespaces_create` returns errors: @@ -233,7 +179,7 @@ If `devpod_up`, `devcontainer_up`, or `codespaces_create` returns errors: - ❌ Do NOT install packages on the host - ❌ Do NOT run builds on the host - ❌ Do NOT modify the host's global config -- ❌ Do NOT run `docker`, `devcontainer`, `devpod`, `gh`, or `wsl` CLI commands directly — use the MCP tools +- ❌ Do NOT run `docker`, `devcontainer`, `devpod`, or `gh` CLI commands directly — use the MCP tools - ✅ DO authenticate before using codespaces tools - ✅ DO ask the user which account/machine type to use - ✅ DO use `devpod_ssh`, `devcontainer_exec`, or `codespaces_ssh` for everything diff --git a/crates/devcontainer-mcp-core/src/cli.rs b/crates/devcontainer-mcp-core/src/cli.rs index 13b1457..cee82ec 100644 --- a/crates/devcontainer-mcp-core/src/cli.rs +++ b/crates/devcontainer-mcp-core/src/cli.rs @@ -29,9 +29,6 @@ pub enum CliBinary { Gcloud, /// Kubernetes CLI Kubectl, - #[cfg(target_os = "windows")] - /// Windows Subsystem for Linux - Wsl, } impl CliBinary { @@ -44,8 +41,6 @@ impl CliBinary { CliBinary::Aws => "aws", CliBinary::Gcloud => "gcloud", CliBinary::Kubectl => "kubectl", - #[cfg(target_os = "windows")] - CliBinary::Wsl => "wsl", } } @@ -58,8 +53,6 @@ impl CliBinary { CliBinary::Aws => Error::AwsCliNotFound, CliBinary::Gcloud => Error::GcloudCliNotFound, CliBinary::Kubectl => Error::KubectlNotFound, - #[cfg(target_os = "windows")] - CliBinary::Wsl => Error::WslNotFound, } } } diff --git a/crates/devcontainer-mcp-core/src/error.rs b/crates/devcontainer-mcp-core/src/error.rs index 549b840..ec3105c 100644 --- a/crates/devcontainer-mcp-core/src/error.rs +++ b/crates/devcontainer-mcp-core/src/error.rs @@ -27,10 +27,6 @@ pub enum Error { #[error("kubectl not found. Install from: https://kubernetes.io/docs/tasks/tools/")] KubectlNotFound, - #[cfg(target_os = "windows")] - #[error("WSL (wsl.exe) not found. WSL must be installed: https://learn.microsoft.com/en-us/windows/wsl/install")] - WslNotFound, - #[error("DevPod command failed (exit code {exit_code}): {stderr}")] DevPodCommand { exit_code: i32, stderr: String }, diff --git a/crates/devcontainer-mcp-core/src/lib.rs b/crates/devcontainer-mcp-core/src/lib.rs index 0177a10..3189d28 100644 --- a/crates/devcontainer-mcp-core/src/lib.rs +++ b/crates/devcontainer-mcp-core/src/lib.rs @@ -6,5 +6,3 @@ pub mod devpod; pub mod docker; pub mod error; pub mod file_ops; -#[cfg(target_os = "windows")] -pub mod wsl; diff --git a/crates/devcontainer-mcp-core/src/wsl.rs b/crates/devcontainer-mcp-core/src/wsl.rs deleted file mode 100644 index 735a051..0000000 --- a/crates/devcontainer-mcp-core/src/wsl.rs +++ /dev/null @@ -1,182 +0,0 @@ -//! WSL (Windows Subsystem for Linux) backend. -//! -//! Wraps the `wsl.exe` CLI to manage WSL distributions, execute commands, -//! and perform file operations inside Linux distros. - -use serde::Serialize; - -use crate::cli::{run_cli, CliBinary, CliOutput}; -use crate::error::{Error, Result}; - -/// A WSL distribution parsed from `wsl --list --verbose`. -#[derive(Debug, Clone, Serialize)] -pub struct WslDistro { - pub name: String, - pub state: String, - pub version: u8, - pub is_default: bool, -} - -/// Run a WSL CLI command with the given args. -async fn run_wsl(args: &[&str], parse_json: bool) -> Result { - run_cli(&CliBinary::Wsl, args, parse_json).await -} - -// --------------------------------------------------------------------------- -// Distribution management -// --------------------------------------------------------------------------- - -/// `wsl --list --verbose` — list installed distributions with state and version. -/// -/// Parses the tabular output into structured [`WslDistro`] entries and returns -/// them as a JSON array in `CliOutput::json`. -pub async fn list() -> Result { - let mut output = run_wsl(&["--list", "--verbose"], false).await?; - - if output.exit_code == 0 { - let distros = parse_list_output(&output.stdout); - output.json = Some(serde_json::to_value(&distros).unwrap_or_default()); - } - - Ok(output) -} - -/// Parse the tabular output of `wsl --list --verbose`. -/// -/// Example output: -/// ```text -/// NAME STATE VERSION -/// * Ubuntu Running 2 -/// Debian Stopped 2 -/// ``` -fn parse_list_output(stdout: &str) -> Vec { - let mut distros = Vec::new(); - - for line in stdout.lines().skip(1) { - let trimmed = line.trim(); - if trimmed.is_empty() { - continue; - } - - let is_default = trimmed.starts_with('*'); - let clean = trimmed.trim_start_matches('*').trim(); - - let parts: Vec<&str> = clean.split_whitespace().collect(); - if parts.len() >= 3 { - if let Ok(version) = parts[parts.len() - 1].parse::() { - let state = parts[parts.len() - 2].to_string(); - let name = parts[..parts.len() - 2].join(" "); - distros.push(WslDistro { - name, - state, - version, - is_default, - }); - } - } - } - - distros -} - -/// `wsl --set-default ` — set the default WSL distribution. -pub async fn set_default(distro: &str) -> Result { - run_wsl(&["--set-default", distro], false).await -} - -/// `wsl --terminate ` — stop a running distribution. -pub async fn terminate(distro: &str) -> Result { - run_wsl(&["--terminate", distro], false).await -} - -/// `wsl --shutdown` — shut down all running WSL distributions. -pub async fn shutdown() -> Result { - run_wsl(&["--shutdown"], false).await -} - -// --------------------------------------------------------------------------- -// Command execution -// --------------------------------------------------------------------------- - -/// `wsl -d -- sh -c ` — execute a command inside a distro. -pub async fn exec(distro: &str, command: &str) -> Result { - run_wsl(&["-d", distro, "--", "sh", "-c", command], false).await -} - -// --------------------------------------------------------------------------- -// File operations -// --------------------------------------------------------------------------- - -/// Read a file from a WSL distro. -pub async fn file_read(distro: &str, path: &str) -> Result { - let cmd = crate::file_ops::read_file_command(path); - exec(distro, &cmd).await -} - -/// Write (create or overwrite) a file in a WSL distro. -pub async fn file_write(distro: &str, path: &str, content: &str) -> Result { - let cmd = crate::file_ops::write_file_command(path, content); - exec(distro, &cmd).await -} - -/// Surgical edit: replace exactly one occurrence of `old_str` with `new_str`. -pub async fn file_edit(distro: &str, path: &str, old_str: &str, new_str: &str) -> Result { - let read_output = file_read(distro, path).await?; - if read_output.exit_code != 0 { - return Err(Error::FileRead(format!( - "Failed to read {path}: {}", - read_output.stderr.trim() - ))); - } - - let modified = crate::file_ops::apply_edit(&read_output.stdout, old_str, new_str)?; - - let write_output = file_write(distro, path, &modified).await?; - if write_output.exit_code != 0 { - return Err(Error::FileEdit(format!( - "Failed to write {path}: {}", - write_output.stderr.trim() - ))); - } - - Ok(format!("Edit applied to {path}")) -} - -/// List directory contents in a WSL distro. -pub async fn file_list(distro: &str, path: &str) -> Result { - let cmd = crate::file_ops::list_dir_command(path); - exec(distro, &cmd).await -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_parse_list_output_typical() { - let output = "\ - NAME STATE VERSION -* Ubuntu Running 2 - Debian Stopped 2 -"; - let distros = parse_list_output(output); - assert_eq!(distros.len(), 2); - - assert_eq!(distros[0].name, "Ubuntu"); - assert_eq!(distros[0].state, "Running"); - assert_eq!(distros[0].version, 2); - assert!(distros[0].is_default); - - assert_eq!(distros[1].name, "Debian"); - assert_eq!(distros[1].state, "Stopped"); - assert_eq!(distros[1].version, 2); - assert!(!distros[1].is_default); - } - - #[test] - fn test_parse_list_output_empty() { - let output = " NAME STATE VERSION\n"; - let distros = parse_list_output(output); - assert!(distros.is_empty()); - } -} diff --git a/crates/devcontainer-mcp/build.rs b/crates/devcontainer-mcp/build.rs index 256ba3f..1fc7a64 100644 --- a/crates/devcontainer-mcp/build.rs +++ b/crates/devcontainer-mcp/build.rs @@ -19,7 +19,6 @@ fn active_tags() -> HashSet { tags.insert("macos".to_string()); tags.insert("windows".to_string()); tags.insert("docker-desktop".to_string()); - tags.insert("wsl".to_string()); tags } diff --git a/crates/devcontainer-mcp/src/tools/mod.rs b/crates/devcontainer-mcp/src/tools/mod.rs index 4e6f4f1..54ed9f3 100644 --- a/crates/devcontainer-mcp/src/tools/mod.rs +++ b/crates/devcontainer-mcp/src/tools/mod.rs @@ -3,8 +3,6 @@ mod codespaces; pub mod common; mod devcontainer; mod devpod; -#[cfg(target_os = "windows")] -mod wsl; use rmcp::handler::server::router::tool::ToolRouter; use rmcp::model::{ServerCapabilities, ServerInfo}; @@ -19,13 +17,10 @@ impl DevContainerMcp { } fn combined_router() -> ToolRouter { - let r = Self::devpod_router() + Self::devpod_router() + Self::devcontainer_router() + Self::codespaces_router() - + Self::auth_router(); - #[cfg(target_os = "windows")] - let r = r + Self::wsl_router(); - r + + Self::auth_router() } } diff --git a/crates/devcontainer-mcp/src/tools/wsl/exec.rs b/crates/devcontainer-mcp/src/tools/wsl/exec.rs deleted file mode 100644 index 92f3a32..0000000 --- a/crates/devcontainer-mcp/src/tools/wsl/exec.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::tools::common::format_output; -use crate::tools::DevContainerMcp; -use devcontainer_mcp_core::wsl; -use rmcp::handler::server::wrapper::Parameters; -use rmcp::{tool, tool_router}; - -#[derive(serde::Deserialize, schemars::JsonSchema)] -struct WslExecParams { - #[schemars(description = "WSL distribution name")] - distro: String, - #[schemars(description = "Command to execute")] - command: String, -} - -#[tool_router(router = wsl_exec_router, vis = "pub(super)")] -impl DevContainerMcp { - #[tool( - name = "wsl_exec", - description = "Execute a command inside a WSL distribution. Returns stdout, stderr, and exit code." - )] - async fn wsl_exec(&self, Parameters(params): Parameters) -> String { - match wsl::exec(¶ms.distro, ¶ms.command).await { - Ok(output) => format_output(&output), - Err(e) => format!("Error: {e}"), - } - } -} diff --git a/crates/devcontainer-mcp/src/tools/wsl/files.rs b/crates/devcontainer-mcp/src/tools/wsl/files.rs deleted file mode 100644 index 25376fa..0000000 --- a/crates/devcontainer-mcp/src/tools/wsl/files.rs +++ /dev/null @@ -1,138 +0,0 @@ -use crate::tools::DevContainerMcp; -use devcontainer_mcp_core::{file_ops, wsl}; -use rmcp::handler::server::wrapper::Parameters; -use rmcp::{tool, tool_router}; - -#[derive(serde::Deserialize, schemars::JsonSchema)] -struct WslFileReadParams { - #[schemars(description = "WSL distribution name")] - distro: String, - #[schemars(description = "Path to the file inside the distribution")] - path: String, - #[serde(default)] - #[schemars(description = "Start line number (1-based, inclusive)")] - start_line: Option, - #[serde(default)] - #[schemars( - description = "End line number (1-based, inclusive). Use -1 or omit for end of file" - )] - end_line: Option, -} - -#[derive(serde::Deserialize, schemars::JsonSchema)] -struct WslFileWriteParams { - #[schemars(description = "WSL distribution name")] - distro: String, - #[schemars(description = "Path to the file inside the distribution")] - path: String, - #[schemars(description = "File content to write")] - content: String, -} - -#[derive(serde::Deserialize, schemars::JsonSchema)] -struct WslFileEditParams { - #[schemars(description = "WSL distribution name")] - distro: String, - #[schemars(description = "Path to the file inside the distribution")] - path: String, - #[schemars(description = "The exact string in the file to replace. Must match exactly once.")] - old_str: String, - #[schemars(description = "The new string to replace old_str with")] - new_str: String, -} - -#[derive(serde::Deserialize, schemars::JsonSchema)] -struct WslFileListParams { - #[schemars(description = "WSL distribution name")] - distro: String, - #[serde(default)] - #[schemars(description = "Path to the directory inside the distribution (defaults to '.')")] - path: Option, -} - -#[tool_router(router = wsl_files_router, vis = "pub(super)")] -impl DevContainerMcp { - #[tool( - name = "wsl_file_read", - description = "Read file content from a WSL distribution. Returns content with line numbers. Supports optional line range." - )] - async fn wsl_file_read(&self, Parameters(params): Parameters) -> String { - match wsl::file_read(¶ms.distro, ¶ms.path).await { - Ok(output) => { - if output.exit_code != 0 { - return format!( - "Error (exit {}): {}", - output.exit_code, - output.stderr.trim() - ); - } - let end = params - .end_line - .and_then(|e| if e < 0 { None } else { Some(e as usize) }); - file_ops::format_with_line_numbers(&output.stdout, params.start_line, end) - } - Err(e) => format!("Error: {e}"), - } - } - - #[tool( - name = "wsl_file_write", - description = "Create or overwrite a file in a WSL distribution. Creates parent directories automatically." - )] - async fn wsl_file_write(&self, Parameters(params): Parameters) -> String { - match wsl::file_write(¶ms.distro, ¶ms.path, ¶ms.content).await { - Ok(output) => { - if output.exit_code != 0 { - format!( - "Error (exit {}): {}", - output.exit_code, - output.stderr.trim() - ) - } else { - format!("File written: {}", params.path) - } - } - Err(e) => format!("Error: {e}"), - } - } - - #[tool( - name = "wsl_file_edit", - description = "Make a surgical edit to a file in a WSL distribution. Replaces exactly one occurrence of old_str with new_str. The old_str must match exactly one location in the file — include enough surrounding context to make it unique." - )] - async fn wsl_file_edit(&self, Parameters(params): Parameters) -> String { - match wsl::file_edit( - ¶ms.distro, - ¶ms.path, - ¶ms.old_str, - ¶ms.new_str, - ) - .await - { - Ok(msg) => msg, - Err(e) => format!("Error: {e}"), - } - } - - #[tool( - name = "wsl_file_list", - description = "List directory contents in a WSL distribution. Shows non-hidden files up to 2 levels deep." - )] - async fn wsl_file_list(&self, Parameters(params): Parameters) -> String { - let dir = params.path.as_deref().unwrap_or("."); - match wsl::file_list(¶ms.distro, dir).await { - Ok(output) => { - if output.exit_code != 0 { - format!( - "Error (exit {}): {}", - output.exit_code, - output.stderr.trim() - ) - } else { - output.stdout - } - } - Err(e) => format!("Error: {e}"), - } - } -} diff --git a/crates/devcontainer-mcp/src/tools/wsl/list.rs b/crates/devcontainer-mcp/src/tools/wsl/list.rs deleted file mode 100644 index c7a28d3..0000000 --- a/crates/devcontainer-mcp/src/tools/wsl/list.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::tools::common::format_output; -use crate::tools::DevContainerMcp; -use devcontainer_mcp_core::wsl; -use rmcp::{tool, tool_router}; - -#[tool_router(router = wsl_list_router, vis = "pub(super)")] -impl DevContainerMcp { - #[tool( - name = "wsl_list", - description = "List WSL distributions with their state and version." - )] - async fn wsl_list(&self) -> String { - match wsl::list().await { - Ok(output) => format_output(&output), - Err(e) => format!("Error: {e}"), - } - } -} diff --git a/crates/devcontainer-mcp/src/tools/wsl/mod.rs b/crates/devcontainer-mcp/src/tools/wsl/mod.rs deleted file mode 100644 index c3f3f60..0000000 --- a/crates/devcontainer-mcp/src/tools/wsl/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -mod exec; -mod files; -mod list; -mod set_default; -mod shutdown; -mod stop; - -use rmcp::handler::server::router::tool::ToolRouter; - -use super::DevContainerMcp; - -impl DevContainerMcp { - pub(super) fn wsl_router() -> ToolRouter { - Self::wsl_list_router() - + Self::wsl_exec_router() - + Self::wsl_stop_router() - + Self::wsl_shutdown_router() - + Self::wsl_set_default_router() - + Self::wsl_files_router() - } -} diff --git a/crates/devcontainer-mcp/src/tools/wsl/set_default.rs b/crates/devcontainer-mcp/src/tools/wsl/set_default.rs deleted file mode 100644 index e407643..0000000 --- a/crates/devcontainer-mcp/src/tools/wsl/set_default.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::tools::common::format_output; -use crate::tools::DevContainerMcp; -use devcontainer_mcp_core::wsl; -use rmcp::handler::server::wrapper::Parameters; -use rmcp::{tool, tool_router}; - -#[derive(serde::Deserialize, schemars::JsonSchema)] -struct WslSetDefaultParams { - #[schemars(description = "WSL distribution name to set as default")] - distro: String, -} - -#[tool_router(router = wsl_set_default_router, vis = "pub(super)")] -impl DevContainerMcp { - #[tool( - name = "wsl_set_default", - description = "Set the default WSL distribution." - )] - async fn wsl_set_default(&self, Parameters(params): Parameters) -> String { - match wsl::set_default(¶ms.distro).await { - Ok(output) => format_output(&output), - Err(e) => format!("Error: {e}"), - } - } -} diff --git a/crates/devcontainer-mcp/src/tools/wsl/shutdown.rs b/crates/devcontainer-mcp/src/tools/wsl/shutdown.rs deleted file mode 100644 index 5266870..0000000 --- a/crates/devcontainer-mcp/src/tools/wsl/shutdown.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::tools::common::format_output; -use crate::tools::DevContainerMcp; -use devcontainer_mcp_core::wsl; -use rmcp::{tool, tool_router}; - -#[tool_router(router = wsl_shutdown_router, vis = "pub(super)")] -impl DevContainerMcp { - #[tool( - name = "wsl_shutdown", - description = "Shut down all running WSL distributions." - )] - async fn wsl_shutdown(&self) -> String { - match wsl::shutdown().await { - Ok(output) => format_output(&output), - Err(e) => format!("Error: {e}"), - } - } -} diff --git a/crates/devcontainer-mcp/src/tools/wsl/stop.rs b/crates/devcontainer-mcp/src/tools/wsl/stop.rs deleted file mode 100644 index f275f55..0000000 --- a/crates/devcontainer-mcp/src/tools/wsl/stop.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::tools::common::format_output; -use crate::tools::DevContainerMcp; -use devcontainer_mcp_core::wsl; -use rmcp::handler::server::wrapper::Parameters; -use rmcp::{tool, tool_router}; - -#[derive(serde::Deserialize, schemars::JsonSchema)] -struct WslTerminateParams { - #[schemars(description = "WSL distribution name to terminate")] - distro: String, -} - -#[tool_router(router = wsl_stop_router, vis = "pub(super)")] -impl DevContainerMcp { - #[tool( - name = "wsl_terminate", - description = "Terminate (stop) a running WSL distribution." - )] - async fn wsl_terminate(&self, Parameters(params): Parameters) -> String { - match wsl::terminate(¶ms.distro).await { - Ok(output) => format_output(&output), - Err(e) => format!("Error: {e}"), - } - } -} diff --git a/skills/_tools/wsl.txt b/skills/_tools/wsl.txt deleted file mode 100644 index 279ee46..0000000 --- a/skills/_tools/wsl.txt +++ /dev/null @@ -1,12 +0,0 @@ ---- -tags: [wsl] ---- -wsl_list -wsl_exec -wsl_terminate -wsl_shutdown -wsl_set_default -wsl_file_read -wsl_file_write -wsl_file_edit -wsl_file_list diff --git a/skills/core-rule.md b/skills/core-rule.md index e08986d..bec14b5 100644 --- a/skills/core-rule.md +++ b/skills/core-rule.md @@ -6,4 +6,4 @@ order: 20 **If a project has `.devcontainer/devcontainer.json`, ALL work MUST happen inside a dev container — never install dependencies, run builds, or execute code directly on the host.** -**Use ONLY the MCP tools listed here.** Do not invoke `docker`, `devcontainer`, `devpod`, `gh`, or `wsl` CLI commands directly — the MCP tools wrap these CLIs with proper error handling, auth resolution, and escaping. Direct CLI usage bypasses these safeguards. This applies even when the user asks to work "directly in WSL" or "not in a devcontainer" — use `wsl_exec` and WSL file tools instead of raw `wsl` commands. +**Use ONLY the MCP tools listed here.** Do not invoke `docker`, `devcontainer`, `devpod`, or `gh` CLI commands directly — the MCP tools wrap these CLIs with proper error handling, auth resolution, and escaping. Direct CLI usage bypasses these safeguards. diff --git a/skills/footer.md b/skills/footer.md index a14a9b5..285c808 100644 --- a/skills/footer.md +++ b/skills/footer.md @@ -7,7 +7,7 @@ order: 90 - ❌ Do NOT install packages on the host - ❌ Do NOT run builds on the host - ❌ Do NOT modify the host's global config -- ❌ Do NOT run `docker`, `devcontainer`, `devpod`, `gh`, or `wsl` CLI commands directly — use the MCP tools +- ❌ Do NOT run `docker`, `devcontainer`, `devpod`, or `gh` CLI commands directly — use the MCP tools - ✅ DO authenticate before using codespaces tools - ✅ DO ask the user which account/machine type to use - ✅ DO use `devpod_ssh`, `devcontainer_exec`, or `codespaces_ssh` for everything diff --git a/skills/wsl.md b/skills/wsl.md deleted file mode 100644 index 4329fd3..0000000 --- a/skills/wsl.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -tags: [wsl] -order: 75 ---- -## Workflow: WSL (Windows only) - -> **Use these tools — not raw `wsl.exe` or PowerShell `wsl` commands.** When a user asks to work "in WSL" or "directly in WSL", use `wsl_exec` and the WSL file tools below — these ARE the way to work in WSL through MCP. - -WSL tools let you clone repos, build projects, and run commands inside any WSL distribution — without devcontainers or Docker. - -### 1. List available distributions -``` -wsl_list() -``` - -### 2. Clone and build a repo in WSL -``` -wsl_exec(distro: "Ubuntu", command: "git clone https://github.com/org/repo.git /home/user/repo") -wsl_exec(distro: "Ubuntu", command: "cd /home/user/repo && cargo build") -``` - -### 3. Execute any command inside a distribution -``` -wsl_exec(distro: "Ubuntu", command: "apt list --installed") -``` - -### 4. Set the default distribution -``` -wsl_set_default(distro: "Ubuntu") -``` - -### 5. Stop a distribution -``` -wsl_terminate(distro: "Ubuntu") -``` - -### 6. Shut down all WSL distributions -``` -wsl_shutdown() -``` - -### File operations in WSL -``` -wsl_file_read(distro: "Ubuntu", path: "/home/user/project/src/main.rs") -wsl_file_write(distro: "Ubuntu", path: "/home/user/file.txt", content: "fn main() {}") -wsl_file_edit(distro: "Ubuntu", path: "/home/user/file.txt", old_str: "fn main", new_str: "fn start") -wsl_file_list(distro: "Ubuntu", path: "/home/user/project") -```