Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions crates/forge_app/src/transformers/strip_working_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ mod tests {

// On Windows, paths are recognized and stripped
#[cfg(windows)]
let expected = ContextSummary::new(vec![SummaryMessage::new(
let expected = ContextSummary::new(vec![SummaryBlock::new(
Role::Assistant,
vec![
SummaryToolCall::read(r"src\main.rs").into(),
Expand Down Expand Up @@ -402,7 +402,7 @@ mod tests {
let actual = StripWorkingDir::new(r"C:\Users\user\project").transform(fixture);

#[cfg(windows)]
let expected = ContextSummary::new(vec![SummaryMessage::new(
let expected = ContextSummary::new(vec![SummaryBlock::new(
Role::Assistant,
vec![
SummaryToolCall::read(r"src\main.rs").into(),
Expand Down Expand Up @@ -435,7 +435,7 @@ mod tests {
let actual = StripWorkingDir::new(r"C:\Users\user\project").transform(fixture);

#[cfg(windows)]
let expected = ContextSummary::new(vec![SummaryMessage::new(
let expected = ContextSummary::new(vec![SummaryBlock::new(
Role::Assistant,
vec![
SummaryToolCall::read(r"src\main.rs").into(),
Expand Down Expand Up @@ -469,7 +469,7 @@ mod tests {
let actual = StripWorkingDir::new(r"\\server\share\project").transform(fixture);

#[cfg(windows)]
let expected = ContextSummary::new(vec![SummaryMessage::new(
let expected = ContextSummary::new(vec![SummaryBlock::new(
Role::Assistant,
vec![
SummaryToolCall::read(r"src\main.rs").into(),
Expand Down Expand Up @@ -498,7 +498,7 @@ mod tests {
let actual = StripWorkingDir::new(r"C:\Users\user\project\").transform(fixture);

#[cfg(windows)]
let expected = ContextSummary::new(vec![SummaryMessage::new(
let expected = ContextSummary::new(vec![SummaryBlock::new(
Role::Assistant,
vec![SummaryToolCall::read(r"src\main.rs").into()],
)]);
Expand Down Expand Up @@ -529,7 +529,7 @@ mod tests {
// On Unix: case-sensitive matching, neither path strips (Windows paths not
// recognized)
#[cfg(windows)]
let expected = ContextSummary::new(vec![SummaryMessage::new(
let expected = ContextSummary::new(vec![SummaryBlock::new(
Role::Assistant,
vec![
SummaryToolCall::read(r"src\main.rs").into(),
Expand Down Expand Up @@ -572,18 +572,18 @@ mod tests {

#[cfg(windows)]
let expected = ContextSummary::new(vec![
SummaryMessage::new(
SummaryBlock::new(
Role::User,
vec![SummaryToolCall::read(r"src\main.rs").into()],
),
SummaryMessage::new(
SummaryBlock::new(
Role::Assistant,
vec![
SummaryToolCall::read(r"src\lib.rs").into(),
SummaryToolCall::update("README.md").into(),
],
),
SummaryMessage::new(Role::User, vec![SummaryToolCall::remove("old.rs").into()]),
SummaryBlock::new(Role::User, vec![SummaryToolCall::remove("old.rs").into()]),
]);

#[cfg(not(windows))]
Expand Down
5 changes: 4 additions & 1 deletion crates/forge_app/src/workspace_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ fn absolutize(base_dir: &Path, path: &str) -> String {
if p.is_absolute() {
path.to_owned()
} else {
base_dir.join(p).to_string_lossy().into_owned()
// Use forward slashes for joined paths to ensure consistent path
// separators across platforms (workspace paths are not OS-specific).
let joined = base_dir.join(p).to_string_lossy().into_owned();
joined.replace('\\', "/")
}
}

Expand Down
5 changes: 4 additions & 1 deletion crates/forge_domain/src/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ mod tests {

#[test]
fn test_create_with_nonexistent_absolute_path() {
// Test with a non-existent absolute path
// Test with a non-existent absolute path (platform-appropriate)
#[cfg(windows)]
let nonexistent_path = PathBuf::from(r"C:\this\path\does\not\exist\file.txt");
#[cfg(not(windows))]
let nonexistent_path = PathBuf::from("/this/path/does/not/exist/file.txt");
let snapshot = Snapshot::create(nonexistent_path.clone()).unwrap();

Expand Down
6 changes: 4 additions & 2 deletions crates/forge_infra/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ mod tests {
};

if cfg!(target_os = "windows") {
expected.stdout = format!("'{}'", expected.stdout);
// cmd.exe does not strip single quotes, so they appear in the output
expected.stdout = "'hello world'\n".to_string();
}

assert_eq!(actual.stdout.trim(), expected.stdout.trim());
Expand Down Expand Up @@ -411,7 +412,8 @@ mod tests {
};

if cfg!(target_os = "windows") {
expected.stdout = format!("'{}'", expected.stdout);
// cmd.exe does not strip single quotes, so they appear in the output
expected.stdout = "'silent test'\n".to_string();
}

// The output should still be captured in the CommandOutput
Expand Down
26 changes: 26 additions & 0 deletions crates/forge_main/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ pub enum TopLevelCommand {
#[command(subcommand, alias = "extension")]
Zsh(ZshCommandGroup),

/// PowerShell integration (native on Windows).
#[command(subcommand, alias = "pwsh")]
Powershell(PowershellCommandGroup),

/// List agents, models, providers, tools, or MCP servers.
List(ListCommandGroup),

Expand Down Expand Up @@ -388,6 +392,28 @@ pub enum ZshCommandGroup {
Keyboard,
}

/// PowerShell integration commands (native on Windows).
#[derive(Subcommand, Debug, Clone)]
pub enum PowershellCommandGroup {
/// Generate shell plugin script for eval.
Plugin,

/// Generate shell theme script for eval.
Theme,

/// Run diagnostics on PowerShell environment.
Doctor,

/// Get rprompt information (ANSI colors for PowerShell prompt).
Rprompt,

/// Setup PowerShell integration by updating $PROFILE.
Setup,

/// Show keyboard shortcuts for PowerShell.
Keyboard,
}

/// Command group for MCP server management.
#[derive(Parser, Debug, Clone)]
pub struct McpCommandGroup {
Expand Down
2 changes: 2 additions & 0 deletions crates/forge_main/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ mod info;
mod input;
mod model;
mod porcelain;
mod powershell;
mod prompt;
mod sandbox;
mod shell;
mod state;
mod stream_renderer;
mod sync_display;
Expand Down
16 changes: 16 additions & 0 deletions crates/forge_main/src/powershell/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//! PowerShell shell integration.
//!
//! This module provides PowerShell-specific functionality including:
//! - Plugin generation and installation
//! - Theme generation
//! - Shell diagnostics
//! - Right prompt (rprompt) display using ANSI escape codes

mod plugin;
mod rprompt;

pub use plugin::{
generate_powershell_plugin, generate_powershell_theme, run_powershell_doctor,
run_powershell_keyboard, setup_powershell_integration,
};
pub use rprompt::PowerShellRPrompt;
Loading
Loading