diff --git a/crates/pi-natives/src/grep.rs b/crates/pi-natives/src/grep.rs index ff022ced8..bb6e03973 100644 --- a/crates/pi-natives/src/grep.rs +++ b/crates/pi-natives/src/grep.rs @@ -35,6 +35,7 @@ use smallvec::SmallVec; use crate::{glob_util, iofs, task}; const MAX_FILE_BYTES: u64 = 4 * 1024 * 1024; +const PCRE2_JIT_ENABLED: bool = !cfg!(target_os = "macos"); /// Output mode for [`search`] and [`grep`] (string values match JS callers). #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -1015,7 +1016,7 @@ fn build_pcre_matcher( .multi_line(multiline) .utf(true) .ucp(true) - .jit_if_available(true); + .jit_if_available(PCRE2_JIT_ENABLED); builder.build(pattern) } @@ -2411,10 +2412,16 @@ mod tests { let root = TempDirGuard::new(); write_file(&root.path().join("lookahead.txt"), "foobar\nfoobaz\n"); write_file(&root.path().join("backreference.txt"), "same same\nsame other\n"); + write_file(&root.path().join("models.txt"), "dim_customers_status_accepted_values\n"); for (pattern, path, line) in [ (r"foo(?=bar)", "lookahead.txt", "foobar"), (r"\b(\w+)\s+\1\b", "backreference.txt", "same same"), + ( + r"(final_incremental_account_id_relationships|dim_customers_status_accepted_values|stg_orders_customer_id_relationships)(?!_[0-9a-f]{32})", + "models.txt", + "dim_customers_status_accepted_values", + ), ] { let mut config = base_grep_config(root.path()); config.pattern = pattern.to_string(); @@ -2428,6 +2435,25 @@ mod tests { assert_eq!(result.matches[0].line, line); } } + + #[cfg(unix)] + #[test] + fn grep_supports_multiline_pcre2_backreferences_and_lookahead() { + let root = TempDirGuard::new(); + write_file(&root.path().join("schema.yml"), " - not_null:\n severity: warn\n"); + + let mut config = base_grep_config(root.path()); + config.pattern = r"^(\s+)- (not_null|unique|accepted_values|relationships|expression_is_true|[a-z_]+):\s*$\n(?!\1 (arguments|config|description|name):)".to_string(); + config.multiline = Some(true); + + let result = grep_sync(config, None, task::CancelToken::default()) + .expect("multiline PCRE2 pattern should search without terminating the process"); + + assert_eq!(result.total_matches, 1); + assert_eq!(result.matches.len(), 1); + assert_eq!(result.matches[0].path, "schema.yml"); + assert_eq!(result.matches[0].line_number, 1); + } #[cfg(unix)] #[test] fn grep_directory_skips_fifo_entries() { diff --git a/crates/pi-uu-grep/src/lib.rs b/crates/pi-uu-grep/src/lib.rs index 4aba22add..facdfa3fd 100644 --- a/crates/pi-uu-grep/src/lib.rs +++ b/crates/pi-uu-grep/src/lib.rs @@ -29,6 +29,7 @@ use grep_searcher::{ BinaryDetection, Searcher, SearcherBuilder, Sink, SinkContext, SinkFinish, SinkMatch, }; pub use rg::run as run_rg; +const PCRE2_JIT_ENABLED: bool = !cfg!(target_os = "macos"); #[derive(Parser, Debug)] #[command( @@ -689,7 +690,7 @@ fn build_matcher( .whole_line(cli.line_regexp) .utf(true) .ucp(true) - .jit_if_available(true); + .jit_if_available(PCRE2_JIT_ENABLED); return builder .build_many(patterns) .map(CompiledMatcher::Pcre) @@ -1615,6 +1616,15 @@ mod tests { assert_eq!(stdout, "foobar\n"); } + #[test] + fn perl_mode_compiles_the_native_grep_crash_pattern() { + let pattern = r"(final_incremental_account_id_relationships|dim_customers_status_accepted_values|stg_orders_customer_id_relationships)(?!_[0-9a-f]{32})"; + let (code, stdout, stderr) = + run_grep(&["-P", pattern], "dim_customers_status_accepted_values\n"); + assert_eq!(code, 0, "{stderr}"); + assert_eq!(stdout, "dim_customers_status_accepted_values\n"); + } + #[test] fn byte_offsets_labels_and_nul_filename_separators_are_rendered() { let (code, stdout, stderr) = run_grep(&["-bn", "hit"], "no\nhit\n"); diff --git a/crates/pi-uu-grep/src/rg.rs b/crates/pi-uu-grep/src/rg.rs index 2ef7e2d3a..101ed2aa1 100644 --- a/crates/pi-uu-grep/src/rg.rs +++ b/crates/pi-uu-grep/src/rg.rs @@ -864,7 +864,7 @@ fn build_pcre_matcher(patterns: &[String], cli: &RgCli) -> Result`) while an unsuppressed async job was still running — a `/vibe` worker turn or a bash `async` job that re-wakes the director after it settles. `EventController.#handleAgentEnd` now skips the idle/loader teardown on a non-terminal `agent_end` (`isTerminal: false`) and only tears down at the true terminal settle ([#7386](https://github.com/can1357/oh-my-pi/issues/7386)). +- Fixed `omp setup python` to validate the same configured or discovered interpreter used by the Python eval runtime. +- Fixed self-update misclassifying glibc Linux hosts with an installed musl loader as musl hosts, which could download an unusable musl binary instead of the glibc release. +- Fixed a crash where opening the Agent Hub after a resume and moving the selection triggered an unbounded `ExtensionExitError` unhandled-rejection storm and exit 129. The postmortem module bound the native hard-exit at first evaluation; when the bundler deferred that evaluation into a `withHostGuard` window it froze the guard's throwing replacement, poisoning every later signal/fatal exit. The native exit is now resolved per call, and the guard stamps its replacement with the native primitive it shadows so mid-guard signals still exit ([#7393](https://github.com/can1357/oh-my-pi/issues/7393)). +- Fixed project-scoped session directories using leading-hyphen names and collapsing distinct paths such as `~/project/hail-mary` and `~/project-hail-mary` into one bucket; directory names now use a portable readable prefix plus the canonical cwd hash, and colliding legacy buckets are split by their recorded session cwd during migration ([#7396](https://github.com/can1357/oh-my-pi/issues/7396)). +- Fixed manual `/shake` leaving the context budget and next pre-turn compaction decision anchored to the stale pre-shake provider token count until another model response arrived. +- Fixed Mnemopi scoped recall reporting "No relevant memories found" when every scoped target failed internally; target failures now warn, and total recall failure preserves the underlying engine error while healthy targets remain available ([#7364](https://github.com/can1357/oh-my-pi/issues/7364)). +- Fixed `skill://` resolution ignoring explicitly configured `skills.customDirectories` entries when a same-named skill existed in a default discovery path: the custom-directory skill now wins as the higher-priority source ([#7190](https://github.com/can1357/oh-my-pi/issues/7190)). +- Fixed image paste failing on Wayland-only Linux sessions by reading PNG clipboard payloads through `wl-paste` before falling back to the native bridge ([#7316](https://github.com/can1357/oh-my-pi/issues/7316)). +- Fixed prewalk switching to the fast model during read-only investigation: `xd://` devices are dispatched through the `write` tool, so a read-only call such as an `lsp` navigation counted as the first edit/write and armed the one-way hand-off mid-planning. Device dispatches now carry the wrapped tool's approval tier and only trigger the switch at a `write`/`exec` tier — read-only `lsp`, `debug` inspection, and internal-URL `ast_edit` calls no longer downgrade the model ([#7312](https://github.com/can1357/oh-my-pi/issues/7312)). ## [17.2.4] - 2026-08-01