Skip to content
Merged
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
52 changes: 50 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Boolean = [
```
crates/
plotnik-cli/ # CLI tool
src/commands/ # Subcommands (debug, docs, langs)
src/commands/ # Subcommands (debug, docs, exec, langs, types)
plotnik-core/ # Common code
plotnik-lib/ # Plotnik as library
src/
Expand All @@ -177,20 +177,68 @@ Run: `cargo run -p plotnik-cli -- <command>`

- `debug` — Inspect queries and source file ASTs
- Example: `cargo run -p plotnik-cli -- debug -q '(foo) @bar'`
- `exec` — Execute query against source, output JSON
- Example: `cargo run -p plotnik-cli -- exec -q '(identifier) @id' -s app.js`
- `types` — Generate TypeScript type definitions from query
- Example: `cargo run -p plotnik-cli -- types -q '(identifier) @id' -l javascript`
- `langs` — List supported languages

Inputs: `-q/--query <Q>`, `--query-file <F>`, `--source <S>`, `-s/--source-file <F>`, `-l/--lang <L>`

Output (inferred from input): `--only-symbols`, `--cst`, `--raw`, `--spans`, `--cardinalities`
### `debug` output flags

- `--only-symbols` — Show only symbol table (requires query)
- `--cst` — Show query CST instead of AST
- `--raw` — Include trivia tokens (whitespace, comments)
- `--spans` — Show source spans
- `--cardinalities` — Show inferred cardinalities
- `--graph` — Show compiled transition graph
- `--graph-raw` — Show unoptimized graph (before epsilon elimination)
- `--types` — Show inferred types

```sh
cargo run -p plotnik-cli -- debug -q '(identifier) @id'
cargo run -p plotnik-cli -- debug -q '(identifier) @id' --only-symbols
cargo run -p plotnik-cli -- debug -q '(identifier) @id' --graph -l javascript
cargo run -p plotnik-cli -- debug -q '(identifier) @id' --types -l javascript
cargo run -p plotnik-cli -- debug -s app.ts
cargo run -p plotnik-cli -- debug -s app.ts --raw
cargo run -p plotnik-cli -- debug -q '(function_declaration) @fn' -s app.ts -l typescript
```

### `exec` output flags

- `--pretty` — Pretty-print JSON output
- `--verbose-nodes` — Include line/column positions in nodes
- `--check` — Validate output against inferred types
- `--entry <NAME>` — Entry point name (definition to match from)

```sh
cargo run -p plotnik-cli -- exec -q '(program (expression_statement (identifier) @name))' --source 'x' -l javascript
cargo run -p plotnik-cli -- exec -q '(identifier) @id' -s app.js --pretty
cargo run -p plotnik-cli -- exec -q '(function_declaration) @fn' -s app.ts -l typescript --verbose-nodes
cargo run -p plotnik-cli -- exec -q '(identifier) @id' -s app.js --check
cargo run -p plotnik-cli -- exec -q '(identifier) @id' -s app.js --verbose-nodes --pretty
cargo run -p plotnik-cli -- exec -q 'A = (identifier) @id B = (string) @str' -s app.js --entry B
```

### `types` output flags

- `--format <FORMAT>` — Output format: `typescript` or `ts` (default: typescript)
- `--root-type <NAME>` — Name for root type of anonymous expressions (default: Query)
- `--verbose-nodes` — Use verbose Node shape (matches `exec --verbose-nodes`)
- `--no-node-type` — Don't emit Node/Point type definitions
- `--no-export` — Don't add `export` keyword to types
- `-o/--output <FILE>` — Write output to file instead of stdout

```sh
cargo run -p plotnik-cli -- types -q '(identifier) @id' -l javascript
cargo run -p plotnik-cli -- types -q 'Func = (function_declaration name: (identifier) @name body: (statement_block) @body)' -l js
cargo run -p plotnik-cli -- types -q '(identifier) @id' -l javascript --verbose-nodes
cargo run -p plotnik-cli -- types -q '(identifier) @id' -l javascript --no-node-type
cargo run -p plotnik-cli -- types -q '(identifier) @id' -l javascript -o types.d.ts
```

# Coding rules

- Avoid nesting logic: prefer early exit in functions (return) and loops (continue/break)
Expand Down
2 changes: 2 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 crates/plotnik-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,5 @@ yaml = ["plotnik-langs/yaml"]
clap = { version = "4.5", features = ["derive"] }
plotnik-langs = { version = "0.1.0", path = "../plotnik-langs", default-features = false }
plotnik-lib = { version = "0.1.0", path = "../plotnik-lib" }
serde_json = "1.0"
tree-sitter = "0.26"
89 changes: 89 additions & 0 deletions crates/plotnik-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,95 @@ pub enum Command {

/// List supported languages
Langs,

/// Execute a query against source code and output JSON
#[command(after_help = r#"EXAMPLES:
plotnik exec -q '(identifier) @id' -s app.js
plotnik exec -q '(identifier) @id' -s app.js --pretty
plotnik exec -q '(function_declaration) @fn' -s app.ts -l typescript --verbose-nodes
plotnik exec -q '(identifier) @id' -s app.js --check
plotnik exec --query-file query.plnk -s app.js --entry FunctionDef"#)]
Exec {
#[command(flatten)]
query: QueryArgs,

#[command(flatten)]
source: SourceArgs,

/// Language for source (required for inline text, inferred from extension otherwise)
#[arg(long, short = 'l', value_name = "LANG")]
lang: Option<String>,

#[command(flatten)]
output: ExecOutputArgs,
},

/// Generate type definitions from a query
#[command(after_help = r#"EXAMPLES:
plotnik types -q '(identifier) @id' -l javascript
plotnik types --query-file query.plnk -l typescript
plotnik types -q '(function_declaration) @fn' -l js --format ts
plotnik types -q '(identifier) @id' -l js --verbose-nodes
plotnik types -q '(identifier) @id' -l js -o types.d.ts

NOTE: Use --verbose-nodes to match `exec --verbose-nodes` output shape."#)]
Types {
#[command(flatten)]
query: QueryArgs,

/// Target language (required)
#[arg(long, short = 'l', value_name = "LANG")]
lang: Option<String>,

#[command(flatten)]
output: TypesOutputArgs,
},
}

#[derive(Args)]
pub struct ExecOutputArgs {
/// Pretty-print JSON output
#[arg(long)]
pub pretty: bool,

/// Include verbose node information (line/column positions)
#[arg(long)]
pub verbose_nodes: bool,

/// Validate output against inferred types
#[arg(long)]
pub check: bool,

/// Entry point name (definition to match from)
#[arg(long, value_name = "NAME")]
pub entry: Option<String>,
}

#[derive(Args)]
pub struct TypesOutputArgs {
/// Output format (typescript, ts)
#[arg(long, default_value = "typescript", value_name = "FORMAT")]
pub format: String,

/// Name for the root type (for anonymous expressions)
#[arg(long, default_value = "Query", value_name = "NAME")]
pub root_type: String,

/// Use verbose node shape (matches exec --verbose-nodes)
#[arg(long)]
pub verbose_nodes: bool,

/// Don't emit Node/Point type definitions
#[arg(long)]
pub no_node_type: bool,

/// Don't export types
#[arg(long)]
pub no_export: bool,

/// Write output to file
#[arg(short = 'o', long, value_name = "FILE")]
pub output: Option<PathBuf>,
}

#[derive(Args)]
Expand Down
10 changes: 8 additions & 2 deletions crates/plotnik-cli/src/commands/debug/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mod source;
pub mod source;

use std::fs;
use std::io::{self, Read};
Expand Down Expand Up @@ -86,7 +86,13 @@ pub fn run(args: DebugArgs) {
if (args.graph || args.graph_raw || args.types)
&& let Some(q) = query.take()
{
let (q, pre_opt_dump) = q.build_graph_with_pre_opt_dump();
// Determine root kind for auto-wrapping
let root_kind = args.lang.as_ref().and_then(|lang_name| {
let lang = resolve_lang_for_link(&Some(lang_name.clone()));
lang.root().and_then(|root_id| lang.node_type_name(root_id))
});

let (q, pre_opt_dump) = q.build_graph_with_pre_opt_dump(root_kind);
let mut needs_separator = false;
if args.graph_raw {
if show_both_graphs {
Expand Down
5 changes: 4 additions & 1 deletion crates/plotnik-cli/src/commands/debug/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ pub fn load_source(text: &Option<String>, file: &Option<PathBuf>) -> String {
.expect("failed to read stdin");
return buf;
}
return fs::read_to_string(path).expect("failed to read source file");
return fs::read_to_string(path).unwrap_or_else(|_| {
eprintln!("error: file not found: {}", path.display());
std::process::exit(1);
});
}
unreachable!()
}
Expand Down
Loading