Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ syn = { version = "2", features = ["full"] }
schemars = { version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = "1.0"
serde_norway = "0.9"
sha2 = "0.10"
syntect = "5"
thiserror = "2.0"
Expand Down
74 changes: 74 additions & 0 deletions crates/merry-cli/src/apply_patch_argument.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//! Shared reader for the file sections an `apply_patch` argument declares.
//!
//! The call detail and the TUI projector both need to know which files a patch
//! names, so section headers are recognized in one place and both readers agree
//! about the set of operations. `merry-tools` owns the grammar that validates
//! and applies a patch; this module only presents the same headers, and it must
//! never be used to decide what a patch does.

/// File operation an `apply_patch` section header declares.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PatchArgumentSectionKind {
/// `*** Add File:` creates the file.
Add,
/// `*** Update File:` edits the file.
Update,
/// `*** Delete File:` removes the file.
Delete,
}

/// Splits a section header into its operation and the path that follows it.
///
/// The path is trimmed, because a header may carry trailing model whitespace.
/// Every other line returns `None`, including another `*** ...` directive such
/// as `*** Begin Patch`, so a caller cannot mistake a directive for a file.
pub(crate) fn section_header(line: &str) -> Option<(PatchArgumentSectionKind, &str)> {
[
("*** Add File: ", PatchArgumentSectionKind::Add),
("*** Update File: ", PatchArgumentSectionKind::Update),
("*** Delete File: ", PatchArgumentSectionKind::Delete),
]
.into_iter()
.find_map(|(marker, kind)| line.strip_prefix(marker).map(|path| (kind, path.trim())))
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn section_headers_name_every_supported_operation() {
assert_eq!(
section_header("*** Add File: notes/new.txt"),
Some((PatchArgumentSectionKind::Add, "notes/new.txt"))
);
assert_eq!(
section_header("*** Update File: dir/note.txt "),
Some((PatchArgumentSectionKind::Update, "dir/note.txt"))
);
assert_eq!(
section_header("*** Delete File: obsolete.txt"),
Some((PatchArgumentSectionKind::Delete, "obsolete.txt"))
);
}

#[test]
fn directives_and_hunk_lines_are_not_section_headers() {
for line in [
"*** Begin Workspace Patch",
"*** End Workspace Patch",
"*** Begin Patch",
"*** End Patch",
"*** Add File:",
"@@ -1,2 +1,3 @@",
"+added",
" context",
] {
assert_eq!(
section_header(line),
None,
"`{line}` must not name a file section"
);
}
}
}
3 changes: 0 additions & 3 deletions crates/merry-cli/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ pub(crate) async fn run(args: &Args, merry_config: Option<&MerryConfig>) -> Resu
environment: environment.clone(),
provider,
model,
allow_hidden_workspace_paths: false,
automatic_compaction: automatic_compaction_config(merry_config).map_err(unexpected)?,
retry_policy,
context_compaction,
Expand Down Expand Up @@ -190,7 +189,6 @@ pub(crate) struct RuntimeInput<'a> {
pub(crate) environment: CommandGenerationEnvironment,
pub(crate) provider: Arc<dyn ModelProvider>,
pub(crate) model: ModelName,
pub(crate) allow_hidden_workspace_paths: bool,
pub(crate) automatic_compaction: AutomaticCompactionConfig,
pub(crate) retry_policy: Option<ModelRetryPolicy>,
pub(crate) context_compaction: Option<RuntimeRoleProviderConfig>,
Expand Down Expand Up @@ -269,7 +267,6 @@ pub(crate) fn build_runtime(input: RuntimeInput<'_>) -> Result<Runtime, CliError
let mut coding_input =
CodingRuntimeInput::read_only(session_id, input.root, input.provider, input.model)
.with_automatic_compaction(input.automatic_compaction)
.with_allow_hidden_workspace_paths(input.allow_hidden_workspace_paths)
.with_skill_roots(input.skill_roots)
.with_extra_tools([cmd_check_command_tool(input.environment)?]);
if let Some(role_provider) = input.context_compaction {
Expand Down
3 changes: 0 additions & 3 deletions crates/merry-cli/src/cmd/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ async fn command_generation_runtime_is_read_only_workspace_only() {
environment: CommandGenerationEnvironment::detect(&workspace),
provider: Arc::new(provider.clone()),
model: ModelName::new("debug-model").expect("valid model name"),
allow_hidden_workspace_paths: false,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
context_compaction: None,
Expand Down Expand Up @@ -182,7 +181,6 @@ async fn generate_command_plan_reads_structured_final_output() {
environment: CommandGenerationEnvironment::detect(&workspace),
provider: Arc::new(provider),
model: ModelName::new("debug-model").expect("valid model name"),
allow_hidden_workspace_paths: false,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
context_compaction: None,
Expand Down Expand Up @@ -245,7 +243,6 @@ async fn cmd_check_command_tool_reports_path_availability() {
environment,
provider: Arc::new(provider),
model: ModelName::new("debug-model").expect("valid model name"),
allow_hidden_workspace_paths: false,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
context_compaction: None,
Expand Down
4 changes: 0 additions & 4 deletions crates/merry-cli/src/coding/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use std::{

#[cfg(test)]
pub(crate) struct CodingRuntimeOptions {
pub(crate) allow_hidden_workspace_paths: bool,
pub(crate) approval_review: Option<RuntimeRoleProviderConfig>,
pub(crate) automatic_compaction: AutomaticCompactionConfig,
pub(crate) retry_policy: Option<ModelRetryPolicy>,
Expand All @@ -37,7 +36,6 @@ pub(crate) struct HeadlessCodingRuntimeInput<'a> {
pub(crate) model: ModelName,
pub(crate) process_backend: ActionProcessBackend,
pub(crate) extra_tools: Vec<RegisteredTool>,
pub(crate) allow_hidden_workspace_paths: bool,
pub(crate) automatic_compaction: AutomaticCompactionConfig,
pub(crate) retry_policy: Option<ModelRetryPolicy>,
pub(crate) context_compaction: Option<RuntimeRoleProviderConfig>,
Expand Down Expand Up @@ -128,7 +126,6 @@ pub(crate) fn build_coding_runtime(
model,
process_backend: options.process_backend,
extra_tools: options.extra_tools,
allow_hidden_workspace_paths: options.allow_hidden_workspace_paths,
automatic_compaction: options.automatic_compaction,
retry_policy: options.retry_policy,
context_compaction: options.context_compaction,
Expand All @@ -152,7 +149,6 @@ fn build_coding_runtime_from_headless_input(
input.process_backend,
)
.with_extra_tools(input.extra_tools)
.with_allow_hidden_workspace_paths(input.allow_hidden_workspace_paths)
.with_automatic_compaction(input.automatic_compaction)
.with_skill_roots(input.skill_roots)
.with_subagents(input.subagents);
Expand Down
1 change: 0 additions & 1 deletion crates/merry-cli/src/coding/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ fn headless_input<'a>(
permissioned_process_runner_factory,
)),
extra_tools: Vec::new(),
allow_hidden_workspace_paths: false,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
context_compaction: None,
Expand Down
2 changes: 0 additions & 2 deletions crates/merry-cli/src/coding/tests/composition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ async fn headless_runtime_uses_coding_agent_profile() {
permissioned_factory,
)),
extra_tools: Vec::new(),
allow_hidden_workspace_paths: false,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
context_compaction: None,
Expand Down Expand Up @@ -150,7 +149,6 @@ async fn headless_runtime_registers_extra_tools() {
runner,
permissioned_factory,
)),
allow_hidden_workspace_paths: false,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
context_compaction: None,
Expand Down
2 changes: 0 additions & 2 deletions crates/merry-cli/src/coding/tests/project_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ async fn coding_projects_root_agents_in_the_stable_prefix() {
Arc::new(provider.clone()),
model_name(),
CodingRuntimeOptions {
allow_hidden_workspace_paths: false,
approval_review: None,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
Expand Down Expand Up @@ -89,7 +88,6 @@ async fn coding_omits_project_rules_when_root_agents_is_missing() {
Arc::new(provider.clone()),
model_name(),
CodingRuntimeOptions {
allow_hidden_workspace_paths: false,
approval_review: None,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
Expand Down
5 changes: 1 addition & 4 deletions crates/merry-cli/src/coding/tests/skills.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ async fn projects_skill_metadata_without_body() {
std::fs::create_dir_all(skill_root.join("demo")).expect("mkdir skill");
std::fs::write(
skill_root.join("demo/SKILL.md"),
"---\nname: demo-skill\ndescription: Use for demo tasks.\n---\n# Demo\nbody sentinel\n",
"---\nname: demo-skill\ndescription: Use for demo tasks.\nmetadata:\n cli_version: \">=1.2.3\"\n requires:\n bins:\n - demo-cli\n---\n# Demo\nbody sentinel\n",
)
.expect("write skill");

Expand All @@ -34,7 +34,6 @@ async fn projects_skill_metadata_without_body() {
Arc::new(provider.clone()),
model_name(),
CodingRuntimeOptions {
allow_hidden_workspace_paths: false,
approval_review: None,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
Expand Down Expand Up @@ -110,7 +109,6 @@ async fn includes_skill_roots_in_workspace_read_tools() {
Arc::new(provider.clone()),
model_name(),
CodingRuntimeOptions {
allow_hidden_workspace_paths: false,
approval_review: None,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
Expand Down Expand Up @@ -156,7 +154,6 @@ async fn allows_missing_default_skill_root() {
Arc::new(provider.clone()),
model_name(),
CodingRuntimeOptions {
allow_hidden_workspace_paths: false,
approval_review: None,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
Expand Down
3 changes: 0 additions & 3 deletions crates/merry-cli/src/coding/tests/subagents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ async fn hides_subagent_tools_by_default() {
Arc::new(provider.clone()),
model_name(),
CodingRuntimeOptions {
allow_hidden_workspace_paths: false,
approval_review: None,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
Expand Down Expand Up @@ -78,7 +77,6 @@ async fn exposes_subagent_tools_when_enabled() {
Arc::new(provider.clone()),
model_name(),
CodingRuntimeOptions {
allow_hidden_workspace_paths: false,
approval_review: None,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
Expand Down Expand Up @@ -177,7 +175,6 @@ async fn subagent_with_narrow_tools_keeps_stable_profile_and_runtime_admission()
Arc::new(provider.clone()),
model_name(),
CodingRuntimeOptions {
allow_hidden_workspace_paths: false,
approval_review: None,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
Expand Down
2 changes: 2 additions & 0 deletions crates/merry-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Merry terminal client and headless agent entrypoint.

mod apply_patch_argument;
mod cli;
mod cli_error;
mod cli_exit;
Expand All @@ -20,6 +21,7 @@ mod runtime_events;
mod sandbox;
mod session_id;
mod testing;
mod text;
mod tool_display;
mod tui;
mod web;
Expand Down
1 change: 0 additions & 1 deletion crates/merry-cli/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,6 @@ pub(crate) async fn run(
model,
process_backend: backend,
extra_tools: mcp.tools,
allow_hidden_workspace_paths: false,
automatic_compaction: automatic_compaction_config(merry_config).map_err(unexpected)?,
retry_policy,
context_compaction,
Expand Down
4 changes: 0 additions & 4 deletions crates/merry-cli/src/run/output_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ async fn writer_prints_final_output_without_event_jsonl() {
provider: Arc::new(provider),
model: model_name(),
extra_tools: Vec::new(),
allow_hidden_workspace_paths: false,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
context_compaction: None,
Expand Down Expand Up @@ -146,7 +145,6 @@ async fn writer_streams_progress_commentary_before_final_output() {
provider: Arc::new(provider),
model: model_name(),
extra_tools: Vec::new(),
allow_hidden_workspace_paths: false,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
context_compaction: None,
Expand Down Expand Up @@ -205,7 +203,6 @@ async fn jsonl_writer_streams_agent_loop_result() {
provider: Arc::new(provider),
model: model_name(),
extra_tools: Vec::new(),
allow_hidden_workspace_paths: false,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
context_compaction: None,
Expand Down Expand Up @@ -271,7 +268,6 @@ async fn writer_returns_incomplete_when_agent_loop_blocks() {
provider: Arc::new(provider),
model: model_name(),
extra_tools: Vec::new(),
allow_hidden_workspace_paths: false,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
context_compaction: None,
Expand Down
1 change: 0 additions & 1 deletion crates/merry-cli/src/run/persistence_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ fn headless_input<'a>(
model: model_name(),
process_backend: fake_process_backend(),
extra_tools: Vec::new(),
allow_hidden_workspace_paths: false,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
context_compaction: None,
Expand Down
1 change: 0 additions & 1 deletion crates/merry-cli/src/run/session_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ fn headless_input<'a>(
model: model_name(),
process_backend: fake_process_backend(),
extra_tools: Vec::new(),
allow_hidden_workspace_paths: false,
automatic_compaction: merry_runtime::AutomaticCompactionConfig::disabled(),
retry_policy: None,
context_compaction: None,
Expand Down
Loading
Loading