Skip to content

filip-mitish/QKI

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rust TUI License Platform

⚡ QKI — Quick Key Inspector

Interactive jq killer with a visual TUI interface

Navigate JSON with arrow keys. Auto-generate jq syntax. Copy to clipboard with a single keystroke.


The Problem

Every backend developer and DevOps engineer faces the same thing daily:

how to filter nested json jq
jq select where key equals
jq get array element by index
jq extract nested object field

jq is a powerful tool. But its syntax is impossible to memorize.

You work with an API, receive a massive JSON response, and need to extract a single field. You open a browser, google jq syntax, copy an example, adapt it to your structure, make a mistake, google again, try once more...

QKI solves this problem once and for all.


The Solution

cat response.json | qki

An interactive interface opens right in your terminal. You see the JSON tree. Navigate with arrow keys. Expand and collapse nodes. Find the field you need. Press Enter. The correct jq path is automatically copied to your clipboard and printed to stdout.

No more googling. No more memorizing syntax. Visual, fast, precise.


Features

Feature Description
Tree navigation JSON is displayed as an interactive tree with indentation
Expand/collapse Objects and arrays can be expanded and collapsed
Auto jq path generation A correct jq path is calculated in real time for every node
Clipboard copy Press Enter and the path is copied to the system clipboard
Stdout output The path is also printed to the terminal for use in pipelines
Pipe support Works via cat file.json | qki
File support Works via qki file.json
Vim keybindings Navigate with h/j/k/l in addition to arrow keys
Special chars in keys Keys with spaces/special characters are automatically quoted
Cross-platform Linux, macOS, Windows

Demo

Input data

{
  "users": [
    {
      "id": 1,
      "name": "Alice",
      "address": {
        "city": "Moscow",
        "zip": "101000"
      },
      "tags": ["admin", "active"]
    },
    {
      "id": 2,
      "name": "Bob",
      "address": {
        "city": "Berlin",
        "zip": "10115"
      },
      "tags": ["user"]
    }
  ],
  "meta": {
    "total": 2,
    "page": 1
  }
}

QKI interface

┌─QKI──────────────────────────────────────────────────────────┐
│>> ▼ root                                                     │
│     ▼ users: [...]                                           │
│       ▶ 0: {...}                                             │
│       ▶ 1: {...}                                             │
│     ▼ meta: {...}                                            │
│       total: 2                                               │
│       page: 1                                                │
│                                                              │
│                                                              │
│                                                              │
├──────────────────────────────────────────────────────────────┤
│ JQ Path: . | Enter: copy & exit | q/Esc: exit               │
└──────────────────────────────────────────────────────────────┘

Navigating to a nested field

Press a few times, then to expand users, then on element 0, then on address, move to city and press Enter:

┌─QKI──────────────────────────────────────────────────────────┐
│   ▼ root                                                     │
│     ▼ users: [...]                                           │
│       ▼ 0: {...}                                             │
│         id: 1                                                │
│         name: "Alice"                                        │
│         ▼ address: {...}                                     │
│>>          city: "Moscow"                                    │
│            zip: "101000"                                     │
│         ▶ tags: [...]                                        │
│       ▶ 1: {...}                                             │
│     ▶ meta: {...}                                            │
├──────────────────────────────────────────────────────────────┤
│ JQ Path: .users[0].address.city | Enter: copy & exit        │
└──────────────────────────────────────────────────────────────┘

Result in clipboard and stdout:

.users[0].address.city

Now you can use it immediately:

cat response.json | jq '.users[0].address.city'
"Moscow"

Installation

From source (recommended)

Requirements: Rust 1.75+ and cargo

git clone https://github.com/filip-mitish/QKI.git
cd QKI
cargo build --release

The binary will be at target/release/qki. Copy it to your PATH:

sudo cp target/release/qki /usr/local/bin/

Or to a local directory:

mkdir -p ~/.local/bin
cp target/release/qki ~/.local/bin/

Make sure ~/.local/bin is in your $PATH:

echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

Via cargo install

cargo install --git https://github.com/filip-mitish/QKI.git

Via NPM

npm install -g qki-cli

The NPM package will automatically compile the binary via cargo build --release during installation. A Rust toolchain is required.


Usage

Basic usage

Read from a file:

qki data.json

Read from stdin (pipe):

cat data.json | qki

Work with APIs:

curl -s https://api.github.com/repos/torvalds/linux | qki

Real-world API examples

GitHub API

curl -s https://api.github.com/users/torvalds | qki

Navigate to public_repos, press Enter, you get:

.public_repos

Docker API

docker inspect nginx | qki

Navigate to the container configuration field you need.

Kubernetes

kubectl get pod my-pod -o json | qki

Visually find the field you need in the pod spec.

AWS CLI

aws ec2 describe-instances | qki

No more remembering the path to Reservations[0].Instances[0].InstanceId.

Terraform

terraform show -json | qki

Navigate through your infrastructure state visually.


Controls

Navigation keys

Key Action
or k Move up in the tree
or j Move down in the tree
or l Expand a node (object/array)
or h Collapse a node or jump to parent
Enter Copy jq path to clipboard and exit
q Exit without copying
Esc Exit without copying

Node icons

Icon Meaning
Expanded object or array
Collapsed object or array
(space) Leaf value (string, number, bool, null)

Current position indicator

The >> symbol to the left of a line shows the currently selected node. The bottom panel displays the full jq path for the current node.


How jq paths are generated

QKI automatically builds a correct jq path for any node regardless of nesting depth and key types.

Regular keys

Keys consisting of letters, digits, and underscores are written with dot notation:

.users
.address.city
.meta.total

Keys with special characters

Keys containing spaces, hyphens, and other special characters are automatically wrapped in quotes:

{
  "my field": "value",
  "some-key": 42,
  "123start": true
}

Generated paths:

."my field"
."some-key"
."123start"

Array elements

Array elements are addressed with square brackets and an index:

.users[0]
.users[1].name
.tags[0]

Nested structures

For deeply nested structures the path is built recursively:

{
  "a": {
    "b": {
      "c": {
        "d": [1, 2, {"e": "deep"}]
      }
    }
  }
}

Path to "deep":

.a.b.c.d[2].e

Mixed structures

{
  "data": [
    {
      "items": [
        {"name": "first"},
        {"name": "second"}
      ]
    }
  ]
}

Path to "second":

.data[0].items[1].name

Architecture

Project structure

QKI/
├── src/
│   └── main.rs          # Main application code
├── bin/
│   └── qki              # NPM wrapper (Node.js)
├── Cargo.toml           # Rust configuration
├── Cargo.lock           # Locked dependencies
├── package.json         # NPM configuration
└── README.md            # Documentation

Dependencies

Crate Version Purpose
ratatui 0.30.0 Terminal UI framework
crossterm 0.29.0 Cross-platform terminal handling
serde_json 1.0.149 JSON parsing
arboard 3.6.1 System clipboard access

Internal design

1. Input parsing

QKI accepts JSON from two sources:

  • stdin (via pipe) — detected using io::stdin().is_terminal()
  • file (command line argument) — via fs::read_to_string

Input data is parsed through serde_json into the Value type.

2. Tree construction

The JSON value is recursively converted into a tree of Node structs. Each node contains:

  • Key (k) — field name or array index
  • Value (v) — displayed value
  • Children (c) — vector of child nodes
  • Expansion state (ex) — expanded or collapsed
  • jq path (p) — full jq path to this node
  • Leaf flag (is_leaf) — whether the node is a terminal value

3. Rendering

On each frame the tree is "flattened" into a linear list of visible items (the flatten function). Collapsed nodes skip their children. The list is rendered through the List widget from ratatui with highlighting of the current element.

4. Input handling

Keyboard events are read through crossterm::event. Non-blocking polling is used with a 16ms timeout (60 FPS). Navigation key presses update the current element index. Pressing Enter saves the path and the loop exits.

5. Clipboard copy

On exit via Enter, the arboard library copies the jq path to the system clipboard. If the clipboard is unavailable (headless server, SSH without X11), the path is still printed to stdout.


Comparison with alternatives

QKI vs jq

Criterion jq QKI
Interactive No Yes
Learning curve Steep Zero
Visual representation No Tree-based TUI
Auto path generation No Yes
Clipboard copy No Yes
Speed for simple tasks Slow (googling) Instant
Filtering & transformation Yes No (different purpose)
Scripting Yes Minimal

QKI does not fully replace jq. QKI helps you find the correct path, which you then use with jq. It is a complementary tool.

QKI vs fx

Criterion fx QKI
Language Go/JS Rust
jq path generation No Yes
Path copy No Automatic
Binary size ~10MB ~3MB
Startup speed Medium Fast
Dependencies Node.js (for some features) None

QKI vs jless

Criterion jless QKI
Language Rust Rust
jq path generation Partial Full
Path copy No Automatic
Search Yes No (planned)
Vim navigation Yes Yes
Focus Viewing Path generation

Pipelines and integration

Usage in scripts

When you press Enter, QKI outputs the path to stdout, which allows it to be used in pipelines:

PATH_EXPR=$(cat data.json | qki)
cat data.json | jq "$PATH_EXPR"

Integration with fzf

cat data.json | jq 'paths | map(tostring) | join(".")' | fzf

QKI is more convenient for this task as it shows context and values.

Shell alias integration

Add to your .bashrc or .zshrc:

alias jqi='qki'
alias jsonpath='qki'

Or a more advanced alias with auto-apply:

jqauto() {
    local path
    path=$(cat "$1" | qki)
    if [ -n "$path" ]; then
        echo "Applying: jq '$path'"
        cat "$1" | jq "$path"
    fi
}

Usage:

jqauto response.json

Integration with curl

apiq() {
    local path
    path=$(curl -s "$1" | qki)
    if [ -n "$path" ]; then
        curl -s "$1" | jq "$path"
    fi
}

Usage:

apiq https://api.github.com/users/torvalds

Building from source

Requirements

  • Rust 1.75 or higher
  • cargo (installed alongside Rust)

Installing Rust:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Debug build

cargo build

Binary: target/debug/qki

Release build

cargo build --release

Binary: target/release/qki

Run tests

cargo test

Check code

cargo clippy

Format code

cargo fmt

NPM package configuration

The NPM package uses a postinstall hook to automatically compile the Rust code:

{
  "scripts": {
    "postinstall": "cargo build --release"
  }
}

When installing via npm install -g qki-cli:

  1. NPM downloads the package
  2. cargo build --release is executed
  3. The binary target/release/qki is created
  4. NPM registers the wrapper bin/qki as a global command
  5. The wrapper launches the compiled Rust binary when invoked

Error handling

QKI implements strict error handling at every stage:

Situation Behavior
No data on stdin and no file argument Prints no data, exit code 1
Error reading stdin Prints err_stdin, exit code 1
Error reading file Prints err_file, exit code 1
Invalid JSON Prints bad json, exit code 1
Terminal initialization error Prints raw err, exit code 1
Alternate screen error Prints altscreen err, exit code 1
Clipboard unavailable Path is printed to stdout only
Rendering error Clean TUI exit

All errors are printed to stderr, keeping stdout clean.


Limitations

  • Does not support filtering or data transformation (that is what jq is for)
  • Does not support search within the tree (planned)
  • Does not support JSON editing
  • Does not support YAML, TOML, or other formats (planned)
  • On very large JSON files (100MB+) parsing delay is possible
  • Clipboard may not work in headless environments without X11/Wayland

Roadmap

v1.1

  • Search by keys and values (/ to start search)
  • Syntax highlighting for values (strings, numbers, bools, null in different colors)
  • Configurable colors via configuration file

v1.2

  • YAML input support
  • TOML input support
  • Multiple path selection
  • Selected data export

v2.0

  • Built-in real-time jq filter
  • Split view: tree on the left, jq result on the right
  • Path selection history
  • Bookmarks for frequently used paths

FAQ

Why yet another JSON viewer?

QKI is not a viewer. It is a tool for generating jq syntax. The core value is automatic creation of the correct path and instant clipboard copy.

Why Rust?

  • Fast startup (critical for CLI tools)
  • Minimal memory usage
  • No runtime dependencies
  • Single static binary

Does it work over SSH?

Yes, but the clipboard will not function without X11 forwarding. The path is still output to stdout, so you can use it manually.

Does it work with large files?

QKI loads the entire JSON into memory. For files up to 50MB it works well. For files above 100MB there may be a parsing delay. For gigabyte-sized files it is recommended to pre-filter with jq.

Can I use it together with jq?

That is exactly the intended workflow. QKI finds the path, jq applies it:

# Step 1: find the path visually
cat data.json | qki
# Copied: .users[0].address.city

# Step 2: apply with jq
cat data.json | jq '.users[0].address.city'

Why the name QKI?

Quick Key Inspector — a fast key inspector for JSON structures.


Contributing

Contributions of any kind are welcome. Here are a few areas where help is needed:

  1. Tree search — implementing / for searching by keys and values
  2. Color highlighting — different colors for JSON value types
  3. YAML/TOML support — automatic format detection
  4. Tests — unit tests for path generation and parsing

How to contribute

git clone https://github.com/filip-mitish/QKI.git
cd QKI
cargo build
cargo run -- test.json

Create a branch, make your changes, open a Pull Request.


License

MIT License

Copyright (c) 2026 tripock

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


QKI — because googling jq syntax every single day is not work, it is suffering.

About

Interactive jq killer with a visual TUI interface

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors