diff --git a/AGENTS.md b/AGENTS.md index c3c53cec..32424a24 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -226,10 +226,17 @@ Options: `--verbose-nodes`, `--no-node-type`, `--no-export`, `-o ` Execute a query against source code and output JSON. +**Usage variants:** +``` +exec # two positional files +exec -q # inline query + source file +exec -q -s -l # all inline +``` + ```sh cargo run -p plotnik-cli -- exec query.ptk app.ts -cargo run -p plotnik-cli -- exec -q '(identifier) @id' app.ts # -q shifts positional to source -cargo run -p plotnik-cli -- exec -q '(identifier) @id' -s 'let x' -l javascript +cargo run -p plotnik-cli -- exec -q 'Q = (identifier) @id' app.ts +cargo run -p plotnik-cli -- exec -q 'Q = (identifier) @id' -s 'let x' -l javascript ``` Options: `--compact`, `--verbose-nodes`, `--check`, `--entry ` @@ -238,9 +245,16 @@ Options: `--compact`, `--verbose-nodes`, `--check`, `--entry ` Trace query execution for debugging. +**Usage variants:** +``` +trace # two positional files +trace -q # inline query + source file +trace -q -s -l # all inline +``` + ```sh cargo run -p plotnik-cli -- trace query.ptk app.ts -cargo run -p plotnik-cli -- trace -q '(identifier) @id' app.ts # -q shifts positional to source +cargo run -p plotnik-cli -- trace -q 'Q = (identifier) @id' app.ts cargo run -p plotnik-cli -- trace query.ptk app.ts --no-result -vv ``` diff --git a/crates/plotnik-cli/src/cli.rs b/crates/plotnik-cli/src/cli.rs index a261d98c..fbfcabc8 100644 --- a/crates/plotnik-cli/src/cli.rs +++ b/crates/plotnik-cli/src/cli.rs @@ -31,10 +31,15 @@ pub struct Cli { #[derive(Subcommand)] pub enum Command { /// Explore a source file's tree-sitter AST - #[command(after_help = r#"EXAMPLES: - plotnik tree app.ts - plotnik tree app.ts --raw - plotnik tree -s 'let x = 1' -l javascript"#)] + #[command( + override_usage = "\ + plotnik tree + plotnik tree -s -l ", + after_help = r#"EXAMPLES: + plotnik tree app.ts # source file + plotnik tree app.ts --raw # include anonymous nodes + plotnik tree -s 'let x = 1' -l js # inline source"# + )] Tree { /// Source file to parse (use "-" for stdin) #[arg(value_name = "SOURCE")] @@ -58,11 +63,17 @@ pub enum Command { }, /// Validate a query - #[command(after_help = r#"EXAMPLES: - plotnik check query.ptk - plotnik check query.ptk -l typescript - plotnik check queries.ts/ - plotnik check -q '(identifier) @id' -l javascript"#)] + #[command( + override_usage = "\ + plotnik check + plotnik check -l + plotnik check -q [-l ]", + after_help = r#"EXAMPLES: + plotnik check query.ptk # validate syntax only + plotnik check query.ptk -l ts # also check against grammar + plotnik check queries.ts/ # workspace directory + plotnik check -q 'Q = ...' -l js # inline query"# + )] Check { /// Query file or workspace directory #[arg(value_name = "QUERY")] @@ -85,10 +96,16 @@ pub enum Command { }, /// Show compiled bytecode - #[command(after_help = r#"EXAMPLES: - plotnik dump query.ptk - plotnik dump query.ptk -l typescript - plotnik dump -q '(identifier) @id'"#)] + #[command( + override_usage = "\ + plotnik dump + plotnik dump -l + plotnik dump -q [-l ]", + after_help = r#"EXAMPLES: + plotnik dump query.ptk # unlinked bytecode + plotnik dump query.ptk -l ts # linked (resolved node types) + plotnik dump -q 'Q = ...' # inline query"# + )] Dump { /// Query file or workspace directory #[arg(value_name = "QUERY")] @@ -107,13 +124,17 @@ pub enum Command { }, /// Generate type definitions from a query - #[command(after_help = r#"EXAMPLES: - plotnik infer query.ptk -l javascript - plotnik infer queries.ts/ -o types.d.ts - plotnik infer -q '(function_declaration) @fn' -l typescript - plotnik infer query.ptk -l js --verbose-nodes - -NOTE: Use --verbose-nodes to match `exec --verbose-nodes` output shape."#)] + #[command( + override_usage = "\ + plotnik infer -l + plotnik infer -q -l ", + after_help = r#"EXAMPLES: + plotnik infer query.ptk -l js # from file + plotnik infer -q 'Q = ...' -l ts # inline query + plotnik infer query.ptk -l js -o types.d.ts # write to file + +NOTE: Use --verbose-nodes to match `exec --verbose-nodes` output shape."# + )] Infer { /// Query file or workspace directory #[arg(value_name = "QUERY")] @@ -135,10 +156,16 @@ NOTE: Use --verbose-nodes to match `exec --verbose-nodes` output shape."#)] }, /// Execute a query against source code and output JSON - #[command(after_help = r#"EXAMPLES: - plotnik exec query.ptk app.js - plotnik exec -q '(identifier) @id' -s 'let x = 1' -l javascript - plotnik exec query.ptk app.ts --compact"#)] + #[command( + override_usage = "\ + plotnik exec + plotnik exec -q + plotnik exec -q -s -l ", + after_help = r#"EXAMPLES: + plotnik exec query.ptk app.js # two positional files + plotnik exec -q 'Q = ...' app.js # inline query + source file + plotnik exec -q 'Q = ...' -s 'let x' -l js # all inline"# + )] Exec { /// Query file or workspace directory #[arg(value_name = "QUERY")] @@ -168,10 +195,16 @@ NOTE: Use --verbose-nodes to match `exec --verbose-nodes` output shape."#)] }, /// Trace query execution for debugging - #[command(after_help = r#"EXAMPLES: - plotnik trace -q '(identifier) @id' -s 'let x = 1' -l javascript - plotnik trace query.ptk app.ts - plotnik trace query.ptk app.ts --no-result"#)] + #[command( + override_usage = "\ + plotnik trace + plotnik trace -q + plotnik trace -q -s -l ", + after_help = r#"EXAMPLES: + plotnik trace query.ptk app.js # two positional files + plotnik trace -q 'Q = ...' app.js # inline query + source file + plotnik trace -q 'Q = ...' -s 'let x' -l js # all inline"# + )] Trace { /// Query file or workspace directory #[arg(value_name = "QUERY")] diff --git a/crates/plotnik-cli/src/main.rs b/crates/plotnik-cli/src/main.rs index 769d39ee..149f0a82 100644 --- a/crates/plotnik-cli/src/main.rs +++ b/crates/plotnik-cli/src/main.rs @@ -1,6 +1,8 @@ mod cli; mod commands; +use std::path::PathBuf; + use cli::{Cli, Command}; use commands::check::CheckArgs; use commands::dump::DumpArgs; @@ -9,6 +11,20 @@ use commands::infer::InferArgs; use commands::trace::TraceArgs; use commands::tree::TreeArgs; +/// When -q is used with a single positional arg, shift it from query to source. +/// This enables: `plotnik exec -q 'query' source.js` +fn shift_positional_to_source( + has_query_text: bool, + query_path: Option, + source_path: Option, +) -> (Option, Option) { + if has_query_text && query_path.is_some() && source_path.is_none() { + (None, query_path) + } else { + (query_path, source_path) + } +} + fn main() { let cli = ::parse(); @@ -85,6 +101,13 @@ fn main() { exec_output, output, } => { + // When -q is used with a single positional, shift it to source + let (query_path, source_path) = shift_positional_to_source( + query_text.is_some(), + query_path, + source_path, + ); + // Pretty by default when stdout is a TTY, unless --compact is passed let pretty = !exec_output.compact && std::io::IsTerminal::is_terminal(&std::io::stdout()); @@ -111,6 +134,13 @@ fn main() { fuel, output, } => { + // When -q is used with a single positional, shift it to source + let (query_path, source_path) = shift_positional_to_source( + query_text.is_some(), + query_path, + source_path, + ); + use plotnik_lib::engine::Verbosity; let verbosity = match verbose { diff --git a/docs/cli.md b/docs/cli.md index a9d7e6fd..eef12974 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -152,61 +152,113 @@ Supported languages (107): Execute a query against source code and output JSON matches. ```sh -# Basic execution -plotnik exec -q 'Q = (identifier) @id' -s app.js +# Two positional arguments: QUERY SOURCE +plotnik exec query.ptk app.js -# Pretty-print JSON -plotnik exec -q 'Q = (identifier) @id' -s app.js --pretty +# Inline query + positional source (most common) +plotnik exec -q 'Q = (identifier) @id' app.js + +# All inline (requires -l) +plotnik exec -q 'Q = (identifier) @id' -s 'let x = 1' -l javascript # Include source positions in output -plotnik exec -q 'Q = (function_declaration) @fn' -s app.ts -l typescript --verbose-nodes +plotnik exec -q 'Q = (identifier) @id' app.ts --verbose-nodes # Start from a specific definition -plotnik exec --query-file query.ptk -s app.js --entry FunctionDef +plotnik exec query.ptk app.js --entry FunctionDef ``` +**Flags:** + +| Flag | Purpose | +| ----------------- | ------------------------------------- | +| `-q, --query` | Inline query text | +| `-s, --source` | Inline source text | +| `-l, --lang` | Language (inferred from file ext) | +| `--compact` | Output compact JSON | +| `--verbose-nodes` | Include line/column in nodes | +| `--check` | Validate output against inferred types| +| `--entry NAME` | Start from specific definition | + --- -## Input Sources +### trace + +Trace query execution for debugging. -### Query Input +```sh +# Inline query + positional source +plotnik trace -q 'Q = (identifier) @id' app.js -Mutually exclusive—pick one: +# Two positional arguments +plotnik trace query.ptk app.js -| Option | Usage | -| ------------------- | ---------------------------------- | -| `-q, --query TEXT` | Inline query text | -| `--query-file FILE` | Read from file (use `-` for stdin) | +# All inline +plotnik trace -q 'Q = (identifier) @id' -s 'let x = 1' -l js + +# Skip result, show only effects +plotnik trace query.ptk app.js --no-result + +# Increase verbosity +plotnik trace query.ptk app.js -v # verbose +plotnik trace query.ptk app.js -vv # very verbose +``` + +**Flags:** + +| Flag | Purpose | +| ------------- | ------------------------------ | +| `-v` | Verbose output | +| `-vv` | Very verbose output | +| `--no-result` | Skip materialization | +| `--fuel N` | Execution fuel limit | +| `--entry` | Start from specific definition | + +--- + +## Input Modes + +### Query-Only Commands (tree, check, dump, infer) + +These commands take a single input. Use either: +- **Positional**: `plotnik tree app.ts` or `plotnik dump query.ptk` +- **Flag**: `plotnik tree -s 'let x' -l js` or `plotnik dump -q 'Q = ...'` + +### Query+Source Commands (exec, trace) -### Source Input +These commands take two inputs. Use any combination: -Mutually exclusive—pick one: +| Pattern | Query from | Source from | +| ------------------------------- | ------------- | -------------- | +| `exec QUERY SOURCE` | 1st positional| 2nd positional | +| `exec -q '...' SOURCE` | `-q` flag | positional | +| `exec -s '...' QUERY -l lang` | positional | `-s` flag | +| `exec -q '...' -s '...' -l lang`| `-q` flag | `-s` flag | -| Option | Usage | -| ------------------------ | ---------------------------------- | -| `--source TEXT` | Inline source code | -| `-s, --source-file FILE` | Read from file (use `-` for stdin) | +**Key rule**: When `-q` is provided with one positional, it becomes SOURCE. -**Language detection:** +### Language Detection -- File input: language inferred from extension (`.ts` → typescript) -- Inline text: requires `-l/--lang` flag +| Input type | Language | +| ------------ | -------------------- | +| File | Inferred from `.ext` | +| Inline (`-s`)| Requires `-l` | --- ## Reading from Stdin -Use `-` as the filename: +Use `-` as the file argument: ```sh # Query from stdin -echo 'Q = (identifier) @id' | plotnik debug --query-file - +echo 'Q = (identifier) @id' | plotnik dump - # Source from stdin -cat app.ts | plotnik debug --source-file - -l typescript +cat app.ts | plotnik tree - -# Pipe query file, source from argument -plotnik infer --query-file - -l javascript < query.ptk +# Exec: query from stdin, source from file +echo 'Q = (identifier) @id' | plotnik exec - app.js ``` ---