Skip to content
Open
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
28 changes: 13 additions & 15 deletions crates/path-cli/src/cmd_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use clap::Parser;
use std::io::IsTerminal;
use std::path::PathBuf;

use crate::config::Config;
use crate::query::Scope;

/// Each array element is a Toolpath step (`step`/`change`/`meta` verbatim)
Expand Down Expand Up @@ -99,10 +100,10 @@ Examples:
path query -r '.[].cache_id' | sort -u # raw ids, pipeable to xargs/grep
path query -r '.[0].change[].structural.text' # read a turn's text, unescaped";

pub fn run(args: QueryArgs, pretty: bool) -> Result<()> {
pub fn run(args: QueryArgs, pretty: bool, config: &Config) -> Result<()> {
#[cfg(not(target_os = "emscripten"))]
if !args.no_sync {
sync_query_scope(&args);
sync_query_scope(&args, config);
}

let scope = Scope {
Expand All @@ -118,30 +119,27 @@ pub fn run(args: QueryArgs, pretty: bool) -> Result<()> {
// pretty on a TTY or when the global `--pretty` flag is set.
let compact = args.compact || (!pretty && !std::io::stdout().is_terminal());

crate::query::run(&scope, &args.filter, compact, args.raw)
crate::query::run(
&scope,
&args.filter,
compact,
args.raw,
config.toolpath_query_explain.as_deref(),
)
}

/// Freshen the slice of the cache this query will read, before reading
/// it. Quiet unless something was actually ingested; a sync failure
/// degrades to querying the cache as-is.
#[cfg(not(target_os = "emscripten"))]
fn sync_query_scope(args: &QueryArgs) {
fn sync_query_scope(args: &QueryArgs, config: &Config) {
let types = sync_types_for(args.source.as_deref(), &args.ids, &args.input);
if types.is_empty() {
return;
}
// Transitional: `query` does not take `&Config` yet; load one for
// the engine. A load failure degrades like a sync failure.
let config = match crate::config::Config::load() {
Ok(config) => config,
Err(e) => {
eprintln!("warning: cache sync skipped: {e}");
return;
}
};
let bundle = crate::harness::HarnessBundle::from_environment();
let bundle = crate::providers::harness_bundle(config);
match crate::sync::sync_bundle(
&config,
config,
&bundle,
&types,
args.project_under.as_deref(),
Expand Down
2 changes: 1 addition & 1 deletion crates/path-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ pub fn run() -> Result<()> {
Commands::Share { args } => cmd_share::run(args),
#[cfg(not(target_os = "emscripten"))]
Commands::Resume { args } => cmd_resume::run(args),
Commands::Query { args } => cmd_query::run(args, cli.pretty),
Commands::Query { args } => cmd_query::run(args, cli.pretty, &config),
Commands::Kind { args } => cmd_kind::run(args),
#[cfg(not(target_os = "emscripten"))]
Commands::Auth { op } => cmd_auth::run(op),
Expand Down
12 changes: 9 additions & 3 deletions crates/path-cli/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,25 @@ pub struct Scope {
/// `filter` is jaq source (`.` emits the array verbatim).
/// `compact` forces single-line JSON; otherwise output is pretty-printed.
/// `raw` prints string results without JSON quoting (like `jq -r`).
/// `explain` is the `$TOOLPATH_QUERY_EXPLAIN` value from [`crate::config::Config`].
///
/// The filter is analyzed once into a [`plan::Plan`]; the executor then streams
/// documents one at a time. An element-wise `.[] | g` filter prints as it goes
/// and holds nothing; a recognized aggregation (`map`, top-N, `length`) holds
/// only its per-file partials — the filter's own output, not the input cache.
/// Anything the planner can't prove decomposable falls back to the whole-array
/// path, which is still lean — the step values are held once, not re-serialized.
pub fn run(scope: &Scope, code: &str, compact: bool, raw: bool) -> Result<()> {
pub fn run(
scope: &Scope,
code: &str,
compact: bool,
raw: bool,
explain: Option<&str>,
) -> Result<()> {
let plan = plan::analyze(code);
// Opt-in observability: `TOOLPATH_QUERY_EXPLAIN=1` reveals the execution
// strategy on stderr. Not a behavioral flag — purely diagnostic.
let explain = std::env::var("TOOLPATH_QUERY_EXPLAIN");
if matches!(explain.as_deref(), Ok(v) if !v.is_empty() && v != "0") {
if matches!(explain, Some(v) if !v.is_empty() && v != "0") {
eprintln!("query plan: {}", plan.describe());
}
// Buffer stdout: the streaming path prints one value per output, and a
Expand Down
Loading