| 59 | } |
| 60 | |
| 61 | pub(super) fn parse_invocation_with_stdin( |
| 62 | def: &ToolDefinition, |
| 63 | args: &[String], |
| 64 | mut read_stdin: impl FnMut() -> Result<String>, |
| 65 | ) -> Result<ParsedInvocation> { |
| 66 | let schema_properties = def |
| 67 | .input_schema |
| 68 | .get("properties") |
| 69 | .and_then(Value::as_object) |
| 70 | .cloned() |
| 71 | .unwrap_or_default(); |
| 72 | |
| 73 | let required = schema_required_keys(def); |
| 74 | |
| 75 | let mut out = ParsedInvocation { |
| 76 | tool_args: Value::Object(Map::new()), |
| 77 | project: None, |
| 78 | raw_json: false, |
| 79 | dry_run: false, |
| 80 | show_help: false, |
| 81 | }; |
| 82 | |
| 83 | let mut explicit_args: Option<Value> = None; |
| 84 | let mut collected: Map<String, Value> = Map::new(); |
| 85 | let mut positionals: Vec<String> = Vec::new(); |
| 86 | |
| 87 | let mut iter = args.iter(); |
| 88 | while let Some(raw) = iter.next() { |
| 89 | // GNU-style `--flag=value` is accepted everywhere clap is, so accept |
| 90 | // it here too: split once on `=` and treat the remainder as the value. |
| 91 | let (flag_part, inline_value): (&str, Option<&str>) = if raw.starts_with("--") { |
| 92 | match raw.split_once('=') { |
| 93 | Some((flag, value)) => (flag, Some(value)), |
| 94 | None => (raw.as_str(), None), |
| 95 | } |
| 96 | } else { |
| 97 | (raw.as_str(), None) |
| 98 | }; |
| 99 | match flag_part { |
| 100 | "-h" | "--help" => { |
| 101 | out.show_help = true; |
| 102 | return Ok(out); |
| 103 | } |
| 104 | "--json" => out.raw_json = true, |
| 105 | "--dry-run" => out.dry_run = true, |
| 106 | "--project" => { |
| 107 | out.project = Some(take_flag_value(&mut iter, "--project", inline_value)?); |
| 108 | } |
| 109 | "--args" => { |
| 110 | // `--args` is a whole-payload arg: inline JSON, `-` for stdin, |
| 111 | // or a file path (bare or `@`-prefixed). Reading from a file or |
| 112 | // stdin sidesteps the kernel's per-argv-string cap |
| 113 | // (MAX_ARG_STRLEN, 128 KiB on Linux) for large payloads. |
| 114 | let raw_args = take_flag_value(&mut iter, "--args", inline_value)?; |
| 115 | let json_str = resolve_args_payload(&raw_args, &mut read_stdin)?; |
| 116 | let value: Value = |
| 117 | serde_json::from_str(&json_str).map_err(|e| TraceDecayError::Config { |
| 118 | message: format!( |