Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:48:40 +08:00
commit 87eb9f0383
12 changed files with 4681 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
{
"name": "cli-ninja",
"description": "Master CLI navigation and code exploration using modern command-line tools (fd, rg, ast-grep, fzf, jq, yq)",
"version": "0.1.1",
"author": {
"name": "pythoninthegrass",
"email": "pythoninthegrass@users.noreply.github.com"
},
"skills": [
"./"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# cli-ninja
Master CLI navigation and code exploration using modern command-line tools (fd, rg, ast-grep, fzf, jq, yq)

540
SKILL.md Normal file
View File

@@ -0,0 +1,540 @@
---
name: cli-ninja
description: Master CLI navigation and code exploration using modern command-line tools. Use this skill when navigating repositories, searching for files/text/code patterns, or working with structured data (JSON/YAML/XML). Provides guidance on fd (file finding), rg (text search), ast-grep (code structure), fzf (interactive selection), jq (JSON), and yq (YAML/XML).
---
# CLI Ninja
## Overview
This skill provides guidance for efficient repository navigation and code exploration using modern CLI tools. Each tool serves a specific purpose: `fd` for finding files, `rg` for searching text, `ast-grep` for understanding code structure, `fzf` for interactive selection, `jq` for JSON manipulation, and `yq` for YAML/XML processing.
## Tool Selection Decision Tree
When navigating or exploring a repository, choose the appropriate tool based on the task:
```
What are you looking for?
├─ FILES by name/path/extension?
│ └─ Use: fd
│ └─ Need to select from results? → Pipe to fzf
├─ TEXT/STRINGS in file contents?
│ └─ Use: rg (ripgrep)
│ └─ Need to select from results? → Pipe to fzf
├─ CODE STRUCTURE (functions, classes, imports)?
│ └─ Use: ast-grep
│ └─ Need to select from results? → Pipe to fzf
│ └─ Need to refactor/replace? → Use ast-grep --rewrite
├─ Working with JSON data?
│ └─ Use: jq
└─ Working with YAML or XML data?
└─ Use: yq
```
## Quick Reference Guide
### fd - Find Files
**Basic patterns:**
```bash
# Find by name
fd 'pattern'
# Find by extension
fd -e py # All Python files
fd -e 'test.py$' # Files ending in test.py
# Find in specific directory
fd 'pattern' src/
# Case-insensitive
fd -i readme
# Hidden files
fd -H '.env'
# Exclude patterns
fd -e py -E '*test*'
```
**Common workflows:**
```bash
# Find and edit a config file interactively
fd config | fzf | xargs $EDITOR
# Find all Python files modified in last 7 days
fd -e py -td --changed-within 7d
# Find files by size
fd -e db --size +100m
```
See `references/fd-patterns.md` for comprehensive patterns and flags.
### rg - Search Text/Strings
**Basic patterns:**
```bash
# Basic search
rg 'search_term'
# Case-insensitive
rg -i 'search_term'
# Whole word only
rg -w 'word'
# With context lines
rg -C 3 'pattern' # 3 lines before/after
rg -B 2 -A 5 'pattern' # 2 before, 5 after
# Specific file types
rg -t py 'import' # Python files
rg -t rust 'fn main' # Rust files
```
**Advanced usage:**
```bash
# Search with line numbers and file names
rg -n 'pattern'
# Multiline search
rg -U 'pattern.*spanning.*lines'
# Exclude patterns
rg 'TODO' -g '!*test*'
# Search in specific directories
rg 'pattern' src/ core/
# JSON output for scripting
rg --json 'pattern'
```
**Interactive workflows:**
```bash
# Search and open in editor
rg -l 'TODO' | fzf | xargs $EDITOR
# Search with preview
rg --line-number 'pattern' | fzf --preview 'bat --color=always {1} --highlight-line {2}'
```
See `references/rg-patterns.md` for regex tips and advanced patterns.
### ast-grep - Find Code Structure
**Philosophy:** ast-grep searches code by structure, not strings. It understands programming language syntax and semantics.
**Basic patterns:**
**Python:**
```bash
# Find all function definitions
ast-grep --pattern 'def $FUNC($$$)' -l py
# Find specific function calls
ast-grep --pattern 'player.play($$$)' -l py
# Find class definitions
ast-grep --pattern 'class $CLASS:' -l py
# Find all imports of a module
ast-grep --pattern 'from $MOD import $$$' -l py
ast-grep --pattern 'import $MOD' -l py
# Find type annotations
ast-grep --pattern 'def $FUNC($$$) -> $TYPE:' -l py
```
**JavaScript/TypeScript:**
```bash
# Find React components
ast-grep --pattern 'function $COMP() { $$$ }' -l tsx
# Find useState hooks
ast-grep --pattern 'const [$STATE, $SETTER] = useState($$$)' -l tsx
# Find async functions
ast-grep --pattern 'async function $FUNC($$$) { $$$ }' -l js
# Find imports
ast-grep --pattern 'import $$ from "$MODULE"' -l ts
```
**Rust:**
```bash
# Find function definitions
ast-grep --pattern 'fn $FUNC($$$) { $$$ }' -l rs
# Find struct definitions
ast-grep --pattern 'struct $NAME { $$$ }' -l rs
# Find impl blocks
ast-grep --pattern 'impl $TRAIT for $TYPE { $$$ }' -l rs
# Find macro usage
ast-grep --pattern 'println!($$$)' -l rs
```
**Go:**
```bash
# Find function definitions
ast-grep --pattern 'func $FUNC($$$) $$$ { $$$ }' -l go
# Find struct definitions
ast-grep --pattern 'type $NAME struct { $$$ }' -l go
# Find interface implementations
ast-grep --pattern 'func ($RECV $TYPE) $METHOD($$$) $$$ { $$$ }' -l go
# Find goroutines
ast-grep --pattern 'go $FUNC($$$)' -l go
```
**Zig:**
```bash
# Find function definitions
ast-grep --pattern 'pub fn $FUNC($$$) $$$ { $$$ }' -l zig
# Find struct definitions
ast-grep --pattern 'const $NAME = struct { $$$ };' -l zig
# Find test functions
ast-grep --pattern 'test "$NAME" { $$$ }' -l zig
# Find error handling
ast-grep --pattern 'try $EXPR' -l zig
```
**Ruby:**
```bash
# Find method definitions
ast-grep --pattern 'def $METHOD($$$); $$$; end' -l rb
# Find class definitions
ast-grep --pattern 'class $CLASS; $$$; end' -l rb
# Find blocks
ast-grep --pattern '$EXPR do |$PARAM|; $$$; end' -l rb
# Find Rails routes
ast-grep --pattern 'get "$PATH", to: "$HANDLER"' -l rb
```
**Refactoring with ast-grep:**
```bash
# Rename a function across the codebase
ast-grep --pattern 'old_function($$$)' --rewrite 'new_function($$$)' -l py --interactive
# Update API calls
ast-grep --pattern 'api.v1.$METHOD($$$)' --rewrite 'api.v2.$METHOD($$$)' -l py
# Modernize code patterns
ast-grep --pattern 'var $VAR = $EXPR' --rewrite 'const $VAR = $EXPR' -l js
```
**Interactive workflows:**
```bash
# Find and select a function to edit
ast-grep --pattern 'def $FUNC($$$)' -l py | fzf | cut -d: -f1 | xargs $EDITOR
# Find all TODOs in code comments (use rg instead)
rg 'TODO|FIXME' | fzf
```
See `references/ast-grep-guide.md` for comprehensive patterns across all languages.
### fzf - Interactive Selection
**Basic usage:**
```bash
# Pipe any list to fzf for selection
command | fzf
# Multi-select mode
command | fzf -m
# With preview window
command | fzf --preview 'bat {}'
```
**Preview configurations:**
```bash
# Preview files with syntax highlighting
fd -t f | fzf --preview 'bat --color=always {}'
# Preview with line numbers
rg --line-number 'pattern' | fzf --preview 'bat --color=always {1} --highlight-line {2}'
# Preview JSON files
fd -e json | fzf --preview 'jq . {}'
# Custom preview window size
fzf --preview 'cat {}' --preview-window=right:50%
```
**Keybindings:**
```bash
# ctrl-t: Paste selected files
# ctrl-r: Paste from history
# alt-c: cd into selected directory
# Custom keybindings
fzf --bind 'ctrl-y:execute-silent(echo {} | pbcopy)'
```
See `references/fzf-workflows.md` for advanced interactive workflows.
### jq - JSON Processing
**Basic queries:**
```bash
# Pretty print
jq . file.json
# Extract field
jq '.name' file.json
# Array indexing
jq '.[0]' file.json
# Nested access
jq '.user.name' file.json
# Array operations
jq '.[] | .name' file.json
```
**Common transformations:**
```bash
# Filter arrays
jq '.users[] | select(.active == true)' file.json
# Map over arrays
jq '.users | map(.name)' file.json
# Sorting
jq '.users | sort_by(.age)' file.json
# Group by
jq 'group_by(.category)' file.json
# Count items
jq '.items | length' file.json
# Keys extraction
jq 'keys' file.json
```
**Advanced usage:**
```bash
# Multiple filters
jq '.users[] | select(.age > 18) | .name' file.json
# Construct new objects
jq '{name: .firstName, email: .emailAddress}' file.json
# Combine multiple files
jq -s '.[0] + .[1]' file1.json file2.json
# Read from stdin
curl -s https://api.example.com/data | jq '.results[]'
```
See `references/jq-cookbook.md` for comprehensive transformations.
### yq - YAML/XML Processing
**YAML queries:**
```bash
# Pretty print
yq . file.yaml
# Extract field
yq '.name' file.yaml
# Nested access
yq '.services.web.image' docker-compose.yml
# Array operations
yq '.dependencies[]' file.yaml
# Convert to JSON
yq -o json . file.yaml
```
**XML queries:**
```bash
# Parse XML
yq -p xml . file.xml
# Extract elements
yq -p xml '.root.element' file.xml
# Convert XML to JSON
yq -p xml -o json . file.xml
```
**Common operations:**
```bash
# Update values
yq '.version = "2.0"' file.yaml
# Merge files
yq '. *= load("other.yaml")' file.yaml
# Filter arrays
yq '.items[] | select(.enabled == true)' file.yaml
```
See `references/yq-examples.md` for YAML/XML processing patterns.
## Combination Workflows
Real-world tasks often combine multiple tools:
### 1. Find and Edit Files with Context
```bash
# Find Python files containing a function, preview them, then edit
rg -l 'def process_data' -t py | fzf --preview 'rg -C 5 "def process_data" {}' | xargs $EDITOR
```
### 2. Code Refactoring Workflow
```bash
# Find all usages of a function across the codebase
ast-grep --pattern 'old_function($$$)' -l py | fzf -m
# Then refactor with interactive confirmation
ast-grep --pattern 'old_function($$$)' --rewrite 'new_function($$$)' -l py --interactive
```
### 3. Interactive Configuration Explorer
```bash
# Find config files and explore their structure
fd -e json -e yaml | fzf --preview 'bat --color=always {} --style=numbers'
# Or with jq/yq preview
fd -e json | fzf --preview 'jq . {}'
fd -e yaml | fzf --preview 'yq . {}'
```
### 4. Find Related Code
```bash
# Find a class definition, then find all its usages
CLASS=$(ast-grep --pattern 'class $NAME:' -l py | fzf | cut -d: -f2)
rg -w "$CLASS" --type py
```
### 5. Dependency Analysis
```bash
# Find all imports of a module
ast-grep --pattern 'from mymodule import $$$' -l py -t python
# Combine with text search for dynamic imports
rg 'importlib.*mymodule' -t py
```
### 6. Multi-File Search and Replace
```bash
# Find files with pattern, select multiple, and apply changes
rg -l 'TODO.*urgent' | fzf -m | xargs sed -i '' 's/TODO.*urgent/DONE/g'
```
### 7. Code Structure Analysis
```bash
# Find all functions in a module, count them
ast-grep --pattern 'def $FUNC($$$)' -l py src/module.py | wc -l
# Find function complexity (functions with many parameters)
ast-grep --pattern 'def $FUNC($A, $B, $C, $D, $E, $$$)' -l py
```
See `scripts/combo-search.sh` and `scripts/interactive-code-finder.sh` for reusable combination workflows.
## Best Practices
### Performance Tips
1. **Use the right tool for the job:**
- File names → `fd`
- Text content → `rg`
- Code structure → `ast-grep`
2. **Narrow your search scope:**
```bash
# Good: Search specific directory
rg 'pattern' src/
# Better: Search specific file types
rg 'pattern' -t py src/
```
3. **Use `.gitignore` respect:**
- Both `fd` and `rg` respect `.gitignore` by default
- Use `-u` or `--no-ignore` to override when needed
### Pattern Matching Tips
1. **ast-grep patterns:**
- Use `$VAR` for single identifiers
- Use `$$$` for arbitrary code sequences
- Use `$$` for statement lists
2. **ripgrep regex:**
- Use `-F` for literal strings (faster)
- Use `-w` for whole words
- Use `\b` for word boundaries
3. **fzf fuzzy matching:**
- Space for AND search: `foo bar` matches files with both
- `|` for OR search: `foo|bar`
- `^` for prefix match, `$` for suffix match
### Debugging Searches
```bash
# See what rg is searching
rg --debug 'pattern' 2>&1 | head -20
# Dry-run ast-grep refactoring
ast-grep --pattern 'old' --rewrite 'new' --interactive
# Test jq queries incrementally
echo '{"name":"test"}' | jq .
echo '{"name":"test"}' | jq '.name'
```
## Resources
This skill includes bundled resources to support CLI navigation:
### scripts/
Example scripts demonstrating tool combinations:
- `combo-search.sh` - Common combination workflows
- `interactive-code-finder.sh` - Interactive ast-grep + fzf workflow
### references/
Comprehensive guides for each tool:
- `fd-patterns.md` - File finding patterns and flags
- `rg-patterns.md` - Text search patterns and regex tips
- `ast-grep-guide.md` - Code structure patterns for Python, Zig, Go, Ruby, JS/TS, Rust
- `fzf-workflows.md` - Interactive selection and preview configurations
- `jq-cookbook.md` - JSON transformations and filters
- `yq-examples.md` - YAML/XML processing examples

77
plugin.lock.json Normal file
View File

@@ -0,0 +1,77 @@
{
"$schema": "internal://schemas/plugin.lock.v1.json",
"pluginId": "gh:pythoninthegrass/cli-ninja:cli-ninja",
"normalized": {
"repo": null,
"ref": "refs/tags/v20251128.0",
"commit": "a20e7b3373b41171100bffe83f9d51875dc770e1",
"treeHash": "f975f1775bfc70bc6ba04673373843287d4c39ec40e06920c8a02aec140c224e",
"generatedAt": "2025-11-28T10:27:42.424911Z",
"toolVersion": "publish_plugins.py@0.2.0"
},
"origin": {
"remote": "git@github.com:zhongweili/42plugin-data.git",
"branch": "master",
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
},
"manifest": {
"name": "cli-ninja",
"description": "Master CLI navigation and code exploration using modern command-line tools (fd, rg, ast-grep, fzf, jq, yq)",
"version": "0.1.1"
},
"content": {
"files": [
{
"path": "README.md",
"sha256": "2b48fd08069259ae5df45b55ac4089b43bcb5c85dbcd248351588c17d18a1623"
},
{
"path": "SKILL.md",
"sha256": "8092b5ec021baf61c1380305c0cba776eb6951bca2dd70a5fbe587938305fe13"
},
{
"path": "references/fzf-workflows.md",
"sha256": "ccb1f98dfd990fcb61e7d22a7b84fc8e39cb71ee18412ba4765d27da95481907"
},
{
"path": "references/yq-examples.md",
"sha256": "c9d285023eb62fc76c7b57a8b80245b41ee2ae12481dcf29d191473c724a4f06"
},
{
"path": "references/ast-grep-guide.md",
"sha256": "a30432790d889a3b3ed72fc6145510f8fcedd7b598830ffca0486d3a7139d25d"
},
{
"path": "references/jq-cookbook.md",
"sha256": "f1035915e749cb214b8ea603d7851e67e5881f1f28b6df6df865a5f778b49ce6"
},
{
"path": "references/fd-patterns.md",
"sha256": "14f1c11aa3a07d4c318ebd9766313ee6683b4ab0f649883b67662f6c6416439f"
},
{
"path": "references/rg-patterns.md",
"sha256": "85aac5efca24cc69cfca9b9a7c594d1f31b625c2f043607fd7d9555ee4a35b09"
},
{
"path": "scripts/interactive-code-finder.sh",
"sha256": "2b545e560b6e29fbae8b070bed84e0ed355151331ae23015fbcc4edd484797ff"
},
{
"path": "scripts/combo-search.sh",
"sha256": "f7e79545b424c43f33e43611388a26e40e62fd0395f35a58217c40c7c5133e05"
},
{
"path": ".claude-plugin/plugin.json",
"sha256": "09558028c80425968afb13e08743fe45a629f86e73bdf71a54effc6f65edefeb"
}
],
"dirSha256": "f975f1775bfc70bc6ba04673373843287d4c39ec40e06920c8a02aec140c224e"
},
"security": {
"scannedAt": null,
"scannerVersion": null,
"flags": []
}
}

View File

@@ -0,0 +1,680 @@
# ast-grep - Code Structure Search Guide
## Overview
`ast-grep` searches code by Abstract Syntax Tree (AST) structure, not text. It understands code semantics and can find patterns that are impossible with regex.
## Core Concepts
### Pattern Syntax
- `$VAR` - Matches a single AST node (identifier, expression, etc.)
- `$$$` - Matches zero or more nodes (variadic)
- `$$` - Matches a sequence of statements
### Pattern Matching Philosophy
ast-grep matches **structure**, not **text**:
```bash
# This finds ALL function calls to 'print', regardless of arguments
ast-grep --pattern 'print($$$)'
# Matches:
# print()
# print("hello")
# print("hello", "world")
# print(x, y, z)
```
## Language Support
ast-grep supports: Python, JavaScript, TypeScript, Rust, Go, Java, C, C++, C#, Ruby, Kotlin, Swift, and more.
---
## Python Patterns
### Function Definitions
```bash
# Any function
ast-grep --pattern 'def $FUNC($$$): $$$' -l py
# Functions with no parameters
ast-grep --pattern 'def $FUNC(): $$$' -l py
# Functions with specific number of parameters
ast-grep --pattern 'def $FUNC($A, $B): $$$' -l py
# Functions with type hints
ast-grep --pattern 'def $FUNC($$$) -> $TYPE: $$$' -l py
# Async functions
ast-grep --pattern 'async def $FUNC($$$): $$$' -l py
# Methods (functions with self)
ast-grep --pattern 'def $METHOD(self, $$$): $$$' -l py
# Class methods
ast-grep --pattern '@classmethod' -A 1 -l py
# Static methods
ast-grep --pattern '@staticmethod' -A 1 -l py
```
### Class Definitions
```bash
# Any class
ast-grep --pattern 'class $CLASS: $$$' -l py
# Class with inheritance
ast-grep --pattern 'class $CLASS($BASE): $$$' -l py
# Class with multiple inheritance
ast-grep --pattern 'class $CLASS($$$): $$$' -l py
# Dataclass
ast-grep --pattern '@dataclass' -A 1 -l py
```
### Imports
```bash
# Import statements
ast-grep --pattern 'import $MODULE' -l py
# From imports
ast-grep --pattern 'from $MODULE import $$$' -l py
# Specific import
ast-grep --pattern 'from $MODULE import $NAME' -l py
# Relative imports
ast-grep --pattern 'from . import $$$' -l py
ast-grep --pattern 'from .. import $$$' -l py
# Import aliases
ast-grep --pattern 'import $MODULE as $ALIAS' -l py
```
### Function Calls
```bash
# Any call to a function
ast-grep --pattern '$FUNC($$$)' -l py
# Method calls
ast-grep --pattern '$OBJ.$METHOD($$$)' -l py
# Chained method calls
ast-grep --pattern '$OBJ.$M1().$M2($$$)' -l py
# Specific function calls
ast-grep --pattern 'print($$$)' -l py
ast-grep --pattern 'open($$$)' -l py
```
### Control Flow
```bash
# If statements
ast-grep --pattern 'if $COND: $$$' -l py
# For loops
ast-grep --pattern 'for $VAR in $ITER: $$$' -l py
# While loops
ast-grep --pattern 'while $COND: $$$' -l py
# Try/except
ast-grep --pattern 'try: $$$ except $EXC: $$$' -l py
# Context managers
ast-grep --pattern 'with $EXPR as $VAR: $$$' -l py
```
### Decorators
```bash
# Any decorator
ast-grep --pattern '@$DECORATOR' -l py
# Specific decorator
ast-grep --pattern '@property' -l py
ast-grep --pattern '@staticmethod' -l py
# Decorator with arguments
ast-grep --pattern '@$DECORATOR($$$)' -l py
```
---
## JavaScript/TypeScript Patterns
### Function Definitions
```bash
# Function declarations
ast-grep --pattern 'function $FUNC($$$) { $$$ }' -l js
# Arrow functions
ast-grep --pattern '($$$) => $BODY' -l js
ast-grep --pattern '$VAR = ($$$) => { $$$ }' -l js
# Async functions
ast-grep --pattern 'async function $FUNC($$$) { $$$ }' -l js
ast-grep --pattern 'async ($$$) => $BODY' -l js
# Generator functions
ast-grep --pattern 'function* $FUNC($$$) { $$$ }' -l js
```
### Class and Components
```bash
# Class declarations
ast-grep --pattern 'class $CLASS { $$$ }' -l js
# Class with extends
ast-grep --pattern 'class $CLASS extends $BASE { $$$ }' -l js
# React functional components
ast-grep --pattern 'function $COMP() { $$$ }' -l tsx
ast-grep --pattern 'const $COMP = () => { $$$ }' -l tsx
# React class components
ast-grep --pattern 'class $COMP extends React.Component { $$$ }' -l tsx
```
### Imports/Exports
```bash
# Import statements
ast-grep --pattern 'import $$ from "$MODULE"' -l js
# Named imports
ast-grep --pattern 'import { $$ } from "$MODULE"' -l js
# Default import
ast-grep --pattern 'import $DEFAULT from "$MODULE"' -l js
# Require (CommonJS)
ast-grep --pattern 'require("$MODULE")' -l js
# Export default
ast-grep --pattern 'export default $EXPR' -l js
# Named exports
ast-grep --pattern 'export { $$ }' -l js
```
### React Hooks
```bash
# useState
ast-grep --pattern 'const [$STATE, $SETTER] = useState($$$)' -l tsx
# useEffect
ast-grep --pattern 'useEffect($$$)' -l tsx
# useCallback
ast-grep --pattern 'useCallback($$$)' -l tsx
# useMemo
ast-grep --pattern 'useMemo($$$)' -l tsx
# Custom hooks (by convention)
ast-grep --pattern 'function use$HOOK($$$) { $$$ }' -l tsx
```
### TypeScript Specifics
```bash
# Type definitions
ast-grep --pattern 'type $NAME = $TYPE' -l ts
# Interface definitions
ast-grep --pattern 'interface $NAME { $$$ }' -l ts
# Type assertions
ast-grep --pattern '$EXPR as $TYPE' -l ts
# Generic functions
ast-grep --pattern 'function $FUNC<$$$>($$$) { $$$ }' -l ts
```
---
## Rust Patterns
### Function Definitions
```bash
# Function declarations
ast-grep --pattern 'fn $FUNC($$$) { $$$ }' -l rs
# Functions with return type
ast-grep --pattern 'fn $FUNC($$$) -> $TYPE { $$$ }' -l rs
# Public functions
ast-grep --pattern 'pub fn $FUNC($$$) { $$$ }' -l rs
# Async functions
ast-grep --pattern 'async fn $FUNC($$$) { $$$ }' -l rs
# Unsafe functions
ast-grep --pattern 'unsafe fn $FUNC($$$) { $$$ }' -l rs
```
### Structs and Enums
```bash
# Struct definitions
ast-grep --pattern 'struct $NAME { $$$ }' -l rs
# Tuple structs
ast-grep --pattern 'struct $NAME($$$);' -l rs
# Enum definitions
ast-grep --pattern 'enum $NAME { $$$ }' -l rs
# Public structs
ast-grep --pattern 'pub struct $NAME { $$$ }' -l rs
```
### Impl Blocks
```bash
# Implementation blocks
ast-grep --pattern 'impl $TYPE { $$$ }' -l rs
# Trait implementations
ast-grep --pattern 'impl $TRAIT for $TYPE { $$$ }' -l rs
# Generic implementations
ast-grep --pattern 'impl<$$$> $TYPE { $$$ }' -l rs
```
### Macros
```bash
# Macro usage
ast-grep --pattern '$MACRO!($$$)' -l rs
# Specific macros
ast-grep --pattern 'println!($$$)' -l rs
ast-grep --pattern 'vec![$$$]' -l rs
# Macro definitions
ast-grep --pattern 'macro_rules! $NAME { $$$ }' -l rs
```
### Error Handling
```bash
# Result returns
ast-grep --pattern 'fn $FUNC($$$) -> Result<$$$> { $$$ }' -l rs
# Unwrap calls
ast-grep --pattern '$EXPR.unwrap()' -l rs
# Question mark operator
ast-grep --pattern '$EXPR?' -l rs
# Match Result
ast-grep --pattern 'match $EXPR { Ok($VAL) => $$$, Err($ERR) => $$$ }' -l rs
```
---
## Go Patterns
### Function Definitions
```bash
# Function declarations
ast-grep --pattern 'func $FUNC($$$) $$$ { $$$ }' -l go
# Methods
ast-grep --pattern 'func ($RECV $TYPE) $METHOD($$$) $$$ { $$$ }' -l go
# Functions with multiple returns
ast-grep --pattern 'func $FUNC($$$) ($$$, error) { $$$ }' -l go
```
### Struct and Interface
```bash
# Struct definitions
ast-grep --pattern 'type $NAME struct { $$$ }' -l go
# Interface definitions
ast-grep --pattern 'type $NAME interface { $$$ }' -l go
# Type aliases
ast-grep --pattern 'type $NAME $TYPE' -l go
```
### Concurrency
```bash
# Goroutine launches
ast-grep --pattern 'go $FUNC($$$)' -l go
# Channel operations
ast-grep --pattern '$CHAN <- $VALUE' -l go
ast-grep --pattern '$VAR := <-$CHAN' -l go
# Select statements
ast-grep --pattern 'select { $$$ }' -l go
```
### Error Handling
```bash
# If err check
ast-grep --pattern 'if err != nil { $$$ }' -l go
# Error returns
ast-grep --pattern 'return $$$, err' -l go
# Defer statements
ast-grep --pattern 'defer $FUNC($$$)' -l go
```
---
## Zig Patterns
### Function Definitions
```bash
# Public functions
ast-grep --pattern 'pub fn $FUNC($$$) $$$ { $$$ }' -l zig
# Private functions
ast-grep --pattern 'fn $FUNC($$$) $$$ { $$$ }' -l zig
# Exported functions
ast-grep --pattern 'export fn $FUNC($$$) $$$ { $$$ }' -l zig
```
### Structs and Types
```bash
# Struct definitions
ast-grep --pattern 'const $NAME = struct { $$$ };' -l zig
# Packed structs
ast-grep --pattern 'const $NAME = packed struct { $$$ };' -l zig
# Type definitions
ast-grep --pattern 'const $NAME = $TYPE;' -l zig
```
### Error Handling
```bash
# Try expressions
ast-grep --pattern 'try $EXPR' -l zig
# Catch
ast-grep --pattern '$EXPR catch $$$' -l zig
# Error unions
ast-grep --pattern 'fn $FUNC($$$) !$$$ { $$$ }' -l zig
```
### Testing
```bash
# Test blocks
ast-grep --pattern 'test "$NAME" { $$$ }' -l zig
# Expect calls
ast-grep --pattern 'try testing.expect($$$)' -l zig
```
---
## Ruby Patterns
### Method Definitions
```bash
# Method definitions
ast-grep --pattern 'def $METHOD($$$); $$$; end' -l rb
# Class methods
ast-grep --pattern 'def self.$METHOD($$$); $$$; end' -l rb
# Private methods
ast-grep --pattern 'private' -A 1 -l rb
```
### Classes and Modules
```bash
# Class definitions
ast-grep --pattern 'class $CLASS; $$$; end' -l rb
# Class with inheritance
ast-grep --pattern 'class $CLASS < $BASE; $$$; end' -l rb
# Module definitions
ast-grep --pattern 'module $MODULE; $$$; end' -l rb
```
### Blocks and Iterators
```bash
# Blocks with do/end
ast-grep --pattern '$EXPR do |$PARAM|; $$$; end' -l rb
# Blocks with braces
ast-grep --pattern '$EXPR { |$PARAM| $$$ }' -l rb
# Each iterator
ast-grep --pattern '$COLLECTION.each do |$ITEM|; $$$; end' -l rb
# Map
ast-grep --pattern '$COLLECTION.map { |$ITEM| $$$ }' -l rb
```
### Rails Patterns
```bash
# Route definitions
ast-grep --pattern 'get "$PATH", to: "$HANDLER"' -l rb
ast-grep --pattern 'post "$PATH", to: "$HANDLER"' -l rb
# ActiveRecord queries
ast-grep --pattern '$MODEL.where($$$)' -l rb
ast-grep --pattern '$MODEL.find_by($$$)' -l rb
# Validations
ast-grep --pattern 'validates $$$' -l rb
```
---
## Advanced Usage
### Refactoring
```bash
# Rename function
ast-grep --pattern 'old_function($$$)' \
--rewrite 'new_function($$$)' \
-l py --interactive
# Update API version
ast-grep --pattern 'api.v1.$METHOD($$$)' \
--rewrite 'api.v2.$METHOD($$$)' \
-l py
# Modernize var to const
ast-grep --pattern 'var $VAR = $EXPR' \
--rewrite 'const $VAR = $EXPR' \
-l js --interactive
# Add type annotations
ast-grep --pattern 'def $FUNC($PARAM):' \
--rewrite 'def $FUNC($PARAM: Any):' \
-l py
```
### Multi-Pattern Search
```bash
# Search with multiple patterns (OR)
ast-grep --pattern 'print($$$)' -l py && \
ast-grep --pattern 'logging.$METHOD($$$)' -l py
# Chain searches (AND)
ast-grep --pattern 'def $FUNC($$$):' -l py | \
grep -v test | \
xargs -I {} ast-grep --pattern 'return $$$' {}
```
### Complex Patterns
```bash
# Find functions with many parameters (code smell)
ast-grep --pattern 'def $FUNC($A, $B, $C, $D, $E, $$$):' -l py
# Find deeply nested function calls
ast-grep --pattern '$A($B($C($$$)))' -l py
# Find unused variables (assigned but not used)
ast-grep --pattern '$VAR = $$$' -l py # Then analyze
# Find mutable default arguments (Python anti-pattern)
ast-grep --pattern 'def $FUNC($PARAM=[]):' -l py
ast-grep --pattern 'def $FUNC($PARAM={}):' -l py
```
### Debugging Patterns
```bash
# Find console.log statements
ast-grep --pattern 'console.log($$$)' -l js
# Find debugger statements
ast-grep --pattern 'debugger' -l js
# Find print debugging
ast-grep --pattern 'print($$$)' -l py
# Find TODO comments in code (not comments, but in strings)
ast-grep --pattern '"TODO: $$$"' -l py
```
## Rule Files
Create reusable rules in YAML:
```yaml
# rules.yml
id: no-mutable-default
language: python
rule:
pattern: 'def $FUNC($$$, $PARAM=[], $$$):'
message: Avoid mutable default arguments
severity: warning
```
Use with:
```bash
ast-grep scan --rule rules.yml
```
## Configuration
Create `.ast-grep.yml` in project root:
```yaml
ruleDirs:
- rules
testDirs:
- tests
ignore:
- node_modules
- .venv
- dist
```
## Performance Tips
1. **Use language specification**: `-l py` is more accurate
2. **Narrow scope**: Search specific directories
3. **Use specific patterns**: More specific = faster
4. **Cache results**: Output to file for repeated analysis
## Interactive Workflows
```bash
# Find and select function to edit
ast-grep --pattern 'def $FUNC($$$):' -l py | \
fzf --preview 'bat --color=always {1}' | \
cut -d: -f1 | \
xargs $EDITOR
# Find usages and refactor
ast-grep --pattern 'old_name($$$)' -l py | \
fzf -m | \
xargs ast-grep --pattern 'old_name($$$)' --rewrite 'new_name($$$)' --interactive
```
## Tips and Tricks
### Finding Security Issues
```bash
# SQL injection risks
ast-grep --pattern 'execute("$$$" + $VAR)' -l py
# XSS risks
ast-grep --pattern 'innerHTML = $$$' -l js
# Command injection
ast-grep --pattern 'os.system($$$)' -l py
```
### Code Quality Analysis
```bash
# Find long functions (structural complexity)
ast-grep --pattern 'def $FUNC($$$): $$$' -l py | \
awk -F: '{print $1":"$2}' | \
xargs -I {} sh -c 'echo {}; sed -n "{}p" | wc -l'
# Find classes with many methods
ast-grep --pattern 'class $CLASS: $$$' -l py
# Find deeply nested code
ast-grep --pattern 'if $A: if $B: if $C: $$$' -l py
```
### Documentation
```bash
# Find undocumented functions
ast-grep --pattern 'def $FUNC($$$):' -l py | \
xargs rg -L '"""' # Functions without docstrings
# Find public APIs
ast-grep --pattern 'export function $FUNC($$$) { $$$ }' -l ts
```
## Comparison with grep/rg
| Task | grep/rg | ast-grep |
|------|---------|----------|
| Find string "print" | `rg print` | N/A |
| Find print function calls | `rg 'print\('` (fragile) | `ast-grep --pattern 'print($$$)'` |
| Find function definitions | `rg '^def '` (limited) | `ast-grep --pattern 'def $F($$$):'` |
| Rename function | Complex sed | `--rewrite` flag |
| Find by structure | Not possible | Native |

290
references/fd-patterns.md Normal file
View File

@@ -0,0 +1,290 @@
# fd - File Finding Patterns
## Overview
`fd` is a fast, user-friendly alternative to `find`. It respects `.gitignore` by default and uses intuitive patterns.
## Basic Usage
```bash
# Find files/directories by name (case-insensitive by default)
fd pattern
# Case-sensitive search
fd -s Pattern
# Case-insensitive explicitly
fd -i pattern
```
## File Type Filtering
```bash
# By extension
fd -e py # All .py files
fd -e js -e ts # Multiple extensions
# By type
fd -t f # Files only
fd -t d # Directories only
fd -t l # Symlinks only
fd -t x # Executable files only
# Combine type and extension
fd -t f -e py # Python files only
```
## Search Scope
```bash
# Search in specific directory
fd pattern /path/to/dir
# Multiple directories
fd pattern dir1/ dir2/
# Depth control
fd -d 1 pattern # Current directory only
fd -d 3 pattern # Up to 3 levels deep
fd --max-depth 2 # Alternative syntax
```
## Advanced Filtering
### By Size
```bash
# Files larger than 100MB
fd -t f --size +100m
# Files smaller than 1KB
fd -t f --size -1k
# Files between 10KB and 1MB
fd -t f --size +10k --size -1m
```
### By Time
```bash
# Modified in last 7 days
fd --changed-within 7d
# Modified more than 30 days ago
fd --changed-before 30d
# Specific date ranges
fd --changed-within 2024-01-01..2024-12-31
```
### Hidden and Ignored Files
```bash
# Include hidden files
fd -H pattern
# Ignore .gitignore rules
fd -u pattern
# Both hidden and ignored
fd -Hu pattern
# Search in .git directories
fd -H -u -I pattern
```
## Exclusion Patterns
```bash
# Exclude specific patterns
fd -E '*.pyc' -E '__pycache__' pattern
# Exclude directories
fd -E 'node_modules' -E '.git' pattern
# Multiple exclusions
fd -e py -E '*test*' -E '*_pb2.py'
```
## Output Formatting
```bash
# Full paths
fd -a pattern
# Relative paths (default)
fd pattern
# Print absolute paths
fd -p pattern
# Null-separated output (for xargs -0)
fd -0 pattern
```
## Pattern Matching
```bash
# Exact name match
fd -g 'config.json'
# Wildcard patterns
fd -g '*.test.js'
fd -g 'test_*.py'
# Regex mode (default)
fd '^[A-Z].*\.py$'
# Fixed string (faster)
fd -F 'literal.string'
```
## Execution
```bash
# Execute command on each result
fd -e py -x python -m py_compile {}
# Execute with multiple files
fd -e txt -X cat
# Execute with placeholders
fd -e md -x echo "File: {}" "Path: {/}" "Dir: {//}"
# Parallel execution
fd -e py -x -j 4 pylint {}
```
## Common Workflows
### Find and Edit
```bash
# Find and edit config files
fd config | fzf | xargs $EDITOR
# Find recent Python files and edit
fd -e py --changed-within 1d | fzf | xargs $EDITOR
```
### Find and Delete
```bash
# Find and remove cache files
fd -e pyc -X rm
fd -t d __pycache__ -X rm -rf
# Interactive deletion
fd '*.log' -x rm -i {}
```
### Find and Copy
```bash
# Copy all Python files to backup
fd -e py -X cp -t backup/
# Copy with directory structure
fd -e py -x cp --parents {} backup/
```
### Statistics and Analysis
```bash
# Count files by type
fd -e py | wc -l
# List largest files
fd -t f --size +1m -x ls -lh {} | sort -k5 -hr
# Find duplicate filenames
fd | awk -F/ '{print $NF}' | sort | uniq -d
```
## Performance Tips
1. **Narrow scope early**: Use `-d` to limit depth, `-t` to filter type
2. **Use specific patterns**: More specific patterns are faster
3. **Respect .gitignore**: Default behavior is already optimized
4. **Use fixed strings**: `-F` is faster than regex when possible
## Common Use Cases
### Development
```bash
# Find test files
fd test -e py
fd -g '*_test.go'
fd -g '*.test.ts'
# Find configuration files
fd -e json -e yaml -e toml config
# Find source files excluding tests
fd -e py -E '*test*'
# Find files modified today
fd --changed-within 1d
```
### Documentation
```bash
# Find all markdown files
fd -e md
# Find README files
fd -i readme
# Find documentation directories
fd -t d docs
```
### Build Artifacts
```bash
# Find compiled files
fd -e o -e so -e dylib
# Find build directories
fd -t d -g 'build' -g 'dist' -g 'target'
# Find and clean
fd -e pyc -E '.venv' -X rm
```
## Comparison with find
| Task | find | fd |
|------|------|-----|
| Find by name | `find . -name '*.py'` | `fd -e py` |
| Files only | `find . -type f` | `fd -t f` |
| Max depth | `find . -maxdepth 2` | `fd -d 2` |
| Exclude pattern | `find . ! -path '*/test/*'` | `fd -E '*test*'` |
| Execute command | `find . -name '*.py' -exec python {} \;` | `fd -e py -x python {}` |
## Tips and Tricks
1. **Combine with other tools**:
```bash
fd -e py | xargs wc -l # Count lines
fd -e md | xargs grip -b # Preview markdown
fd -0 | xargs -0 tar czf # Create archive
```
2. **Use shell aliases**:
```bash
alias fdd='fd -t d' # Directories only
alias fdf='fd -t f' # Files only
alias fdh='fd -H' # Include hidden
```
3. **Create functions**:
```bash
# Find and cd into directory
fcd() { cd $(fd -t d "$1" | fzf) }
# Find and open in editor
fe() { $EDITOR $(fd "$1" | fzf) }
```

589
references/fzf-workflows.md Normal file
View File

@@ -0,0 +1,589 @@
# fzf - Interactive Selection Workflows
## Overview
`fzf` is a command-line fuzzy finder that enables interactive selection from lists. It's a powerful tool for building interactive workflows.
## Basic Usage
```bash
# Pipe any list to fzf
ls | fzf
# Multi-select with Tab
ls | fzf -m
# Use selected result
SELECTED=$(ls | fzf)
echo "You selected: $SELECTED"
```
## Preview Windows
### Basic Preview
```bash
# Preview with cat
fd | fzf --preview 'cat {}'
# Preview with bat (syntax highlighting)
fd | fzf --preview 'bat --color=always {}'
# Preview with line numbers
fd | fzf --preview 'bat --color=always --line-range :500 {}'
```
### Preview Window Configuration
```bash
# Position and size
fzf --preview 'cat {}' --preview-window=right:50%
fzf --preview 'cat {}' --preview-window=up:40%
fzf --preview 'cat {}' --preview-window=down:30%
fzf --preview 'cat {}' --preview-window=left:50%
# Hidden by default (toggle with ctrl-/)
fzf --preview 'cat {}' --preview-window=hidden
# Wrap text
fzf --preview 'cat {}' --preview-window=wrap
# No wrap (default)
fzf --preview 'cat {}' --preview-window=nowrap
```
### Advanced Previews
```bash
# Preview with highlighted search term
rg --line-number 'pattern' | \
fzf --delimiter : \
--preview 'bat --color=always {1} --highlight-line {2}'
# Preview JSON with jq
fd -e json | fzf --preview 'jq --color-output . {}'
# Preview images (requires chafa/imgcat)
fd -e png -e jpg | fzf --preview 'chafa {}'
# Preview directories
fd -t d | fzf --preview 'ls -la {}'
# Preview with tree
fd -t d | fzf --preview 'tree -L 2 {}'
# Multi-level preview (file type detection)
fd | fzf --preview '[[ -f {} ]] && bat --color=always {} || tree -L 1 {}'
```
## Keybindings
### Default Keybindings
```
ctrl-j / ctrl-n / down : Move down
ctrl-k / ctrl-p / up : Move up
enter : Select and exit
tab : Mark item (multi-select)
shift-tab : Unmark item
ctrl-c / esc : Exit without selection
ctrl-/ : Toggle preview window
```
### Custom Keybindings
```bash
# Copy to clipboard (macOS)
fzf --bind 'ctrl-y:execute-silent(echo {} | pbcopy)'
# Copy to clipboard (Linux)
fzf --bind 'ctrl-y:execute-silent(echo {} | xclip -selection clipboard)'
# Open in editor
fzf --bind 'ctrl-e:execute($EDITOR {})'
# Open in editor and exit
fzf --bind 'ctrl-e:execute($EDITOR {})+abort'
# Delete file with confirmation
fzf --bind 'ctrl-d:execute(rm -i {})'
# Multiple bindings
fzf --bind 'ctrl-e:execute($EDITOR {})' \
--bind 'ctrl-y:execute-silent(echo {} | pbcopy)' \
--bind 'ctrl-d:execute(rm -i {})'
```
### Advanced Keybinding Actions
```bash
# Reload results
fzf --bind 'ctrl-r:reload(fd)'
# Change preview
fzf --preview 'bat {}' \
--bind 'ctrl-/:change-preview-window(hidden|)'
# Toggle between preview styles
fzf --preview 'bat --color=always {}' \
--bind 'ctrl-t:change-preview(tree -L 2 {})'
# Accept and execute
fzf --bind 'enter:execute(echo Selected: {})+accept'
```
## Search Syntax
### Fuzzy Matching
```
# Basic fuzzy search
abc # Matches files containing 'a', 'b', 'c' in order
# Examples: abc, aXXbXXc, axbxc
# Exact match (single quote)
'abc # Matches files containing exact string "abc"
# Prefix match (caret)
^abc # Matches files starting with "abc"
# Suffix match (dollar)
abc$ # Matches files ending with "abc"
# Exact match (both)
^abc$ # Matches exactly "abc"
```
### Logical Operators
```
# AND (space)
abc def # Contains both "abc" AND "def"
# OR (pipe)
abc|def # Contains "abc" OR "def"
# NOT (exclamation)
abc !def # Contains "abc" but NOT "def"
# Combination
abc def|ghi !jkl
# Contains "abc" AND ("def" OR "ghi") but NOT "jkl"
```
### Field Selection
```bash
# Search specific field (delimiter-separated)
# -d : Set delimiter
# -n : Select field number (1-indexed)
# --with-nth : Display specific fields
# Example: search only filenames in paths
fd | fzf -d / --with-nth -1
# Example: search only line numbers
rg --line-number 'pattern' | fzf -d : --with-nth 2
```
## Common Workflows
### File Navigation
```bash
# Find and edit file
fe() {
local file
file=$(fd --type f | fzf --preview 'bat --color=always {}')
[[ -n "$file" ]] && $EDITOR "$file"
}
# Find and cd to directory
fcd() {
local dir
dir=$(fd --type d | fzf --preview 'tree -L 1 {}')
[[ -n "$dir" ]] && cd "$dir"
}
# Find file with content preview
ff() {
local file
file=$(rg --files-with-matches "${1:-.}" | \
fzf --preview "rg --color=always --context 5 '$1' {}")
[[ -n "$file" ]] && $EDITOR "$file"
}
```
### Code Search
```bash
# Search content and edit
fzf_grep() {
local line
line=$(rg --line-number "$1" | \
fzf --delimiter : \
--preview 'bat --color=always {1} --highlight-line {2}' \
--preview-window +{2}-/2)
if [[ -n "$line" ]]; then
local file=$(echo "$line" | cut -d: -f1)
local lineno=$(echo "$line" | cut -d: -f2)
$EDITOR "+$lineno" "$file"
fi
}
# Find function definition and edit
find_function() {
local result
result=$(ast-grep --pattern "def $1(\$\$\$):" | \
fzf --preview 'bat --color=always {1}')
if [[ -n "$result" ]]; then
local file=$(echo "$result" | cut -d: -f1)
$EDITOR "$file"
fi
}
```
### Git Integration
```bash
# Interactive git add
fga() {
git status --short | \
fzf -m --preview 'git diff --color=always {2}' | \
awk '{print $2}' | \
xargs git add
}
# Interactive git checkout
fgco() {
git branch --all | \
grep -v HEAD | \
sed 's/^[* ]*//' | \
sed 's#remotes/origin/##' | \
sort -u | \
fzf --preview 'git log --oneline --color=always {}' | \
xargs git checkout
}
# Interactive git log
fgl() {
git log --oneline --color=always | \
fzf --ansi --preview 'git show --color=always {1}' | \
awk '{print $1}' | \
xargs git show
}
# Interactive git diff
fgd() {
git diff --name-only | \
fzf --preview 'git diff --color=always {}' | \
xargs git diff
}
```
### Process Management
```bash
# Interactive kill
fkill() {
local pid
pid=$(ps aux | \
sed 1d | \
fzf -m --header='Select process to kill' | \
awk '{print $2}')
if [[ -n "$pid" ]]; then
echo "$pid" | xargs kill -${1:-9}
fi
}
# Interactive lsof (find what's using a port)
fport() {
local port
port=$(lsof -i -P -n | \
sed 1d | \
fzf --header='Select connection' | \
awk '{print $2}')
[[ -n "$port" ]] && echo "PID: $port"
}
```
### Docker Integration
```bash
# Interactive container selection
fdocker() {
docker ps -a | \
sed 1d | \
fzf --header='Select container' | \
awk '{print $1}'
}
# Interactive container logs
fdlogs() {
local container
container=$(fdocker)
[[ -n "$container" ]] && docker logs -f "$container"
}
# Interactive container exec
fdexec() {
local container
container=$(fdocker)
[[ -n "$container" ]] && docker exec -it "$container" /bin/bash
}
```
### Package Management
```bash
# Interactive npm script runner
fnpm() {
local script
script=$(cat package.json | \
jq -r '.scripts | keys[]' | \
fzf --preview 'jq -r ".scripts.{}" package.json')
[[ -n "$script" ]] && npm run "$script"
}
# Interactive pip package info
fpip() {
pip list | \
fzf --preview 'pip show {1}'
}
```
### Configuration Files
```bash
# Edit config file
fconf() {
local file
file=$(fd -H -t f \
-e conf -e config -e json -e yaml -e yml -e toml -e ini \
. "$HOME/.config" | \
fzf --preview 'bat --color=always {}')
[[ -n "$file" ]] && $EDITOR "$file"
}
# Edit dotfile
fdot() {
local file
file=$(fd -H -t f '^\..*' "$HOME" --max-depth 1 | \
fzf --preview 'bat --color=always {}')
[[ -n "$file" ]] && $EDITOR "$file"
}
```
## Advanced Configurations
### Color Schemes
```bash
# Monokai
export FZF_DEFAULT_OPTS='
--color=fg:#f8f8f2,bg:#272822,hl:#66d9ef
--color=fg+:#f8f8f2,bg+:#44475a,hl+:#66d9ef
--color=info:#a6e22e,prompt:#f92672,pointer:#f92672
--color=marker:#f92672,spinner:#a6e22e,header:#6272a4
'
# Nord
export FZF_DEFAULT_OPTS='
--color=fg:#e5e9f0,bg:#3b4252,hl:#81a1c1
--color=fg+:#e5e9f0,bg+:#434c5e,hl+:#81a1c1
--color=info:#eacb8a,prompt:#bf616a,pointer:#b48ead
--color=marker:#a3be8b,spinner:#b48ead,header:#a3be8b
'
# Tokyo Night
export FZF_DEFAULT_OPTS='
--color=fg:#c0caf5,bg:#1a1b26,hl:#bb9af7
--color=fg+:#c0caf5,bg+:#292e42,hl+:#7dcfff
--color=info:#7aa2f7,prompt:#7dcfff,pointer:#7dcfff
--color=marker:#9ece6a,spinner:#9ece6a,header:#9ece6a
'
```
### Layout Options
```bash
# Reverse layout (prompt at top)
fzf --reverse
# Full screen
fzf --height=100%
# 40% of screen
fzf --height=40%
# Border
fzf --border
fzf --border=rounded
fzf --border=sharp
# Inline info
fzf --inline-info
# No info
fzf --no-info
```
### Performance Options
```bash
# Limit results
fzf --select-1 # Auto-select if only one match
fzf --exit-0 # Exit if no match
# Algorithm
fzf --algo=v1 # Faster, less accurate
fzf --algo=v2 # Default, balanced
# History
fzf --history=/tmp/fzf-history
```
## Environment Variables
```bash
# Default command
export FZF_DEFAULT_COMMAND='fd --type f --hidden --exclude .git'
# Default options
export FZF_DEFAULT_OPTS='
--height 40%
--layout=reverse
--border
--inline-info
--preview "bat --color=always --line-range :500 {}"
--bind ctrl-/:toggle-preview
'
# ctrl-t command (file widget)
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
export FZF_CTRL_T_OPTS="
--preview 'bat --color=always --line-range :500 {}'
--bind 'ctrl-/:toggle-preview'
"
# alt-c command (directory widget)
export FZF_ALT_C_COMMAND='fd --type d --hidden --exclude .git'
export FZF_ALT_C_OPTS="
--preview 'tree -L 1 {}'
"
```
## Shell Integration
### Bash/Zsh Key Bindings
```bash
# Add to ~/.bashrc or ~/.zshrc
source /usr/share/doc/fzf/examples/key-bindings.bash # Bash
source /usr/share/doc/fzf/examples/key-bindings.zsh # Zsh
# Or via package manager
eval "$(fzf --bash)" # Bash
eval "$(fzf --zsh)" # Zsh
```
Default bindings:
- `ctrl-t`: Paste selected files
- `ctrl-r`: Paste from command history
- `alt-c`: cd into selected directory
### Custom Shell Functions
```bash
# Add to ~/.bashrc or ~/.zshrc
# fh - repeat history
fh() {
print -z $( ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | \
fzf +s --tac | \
sed -E 's/ *[0-9]*\*? *//' | \
sed -E 's/\\/\\\\/g')
}
# fbr - checkout git branch
fbr() {
local branches branch
branches=$(git branch -vv) &&
branch=$(echo "$branches" | fzf +m) &&
git checkout $(echo "$branch" | awk '{print $1}' | sed "s/.* //")
}
# fshow - git commit browser
fshow() {
git log --graph --color=always \
--format="%C(auto)%h%d %s %C(black)%C(bold)%cr" "$@" |
fzf --ansi --no-sort --reverse --tiebreak=index --bind=ctrl-s:toggle-sort \
--bind "ctrl-m:execute:
(grep -o '[a-f0-9]\{7\}' | head -1 |
xargs -I % sh -c 'git show --color=always % | less -R') << 'FZF-EOF'
{}
FZF-EOF"
}
```
## Tips and Tricks
### Performance
1. Use fd instead of find for FZF_DEFAULT_COMMAND
2. Limit preview size with --line-range
3. Use --select-1 and --exit-0 for automation
4. Cache results for repeated searches
### User Experience
1. Always provide preview when possible
2. Use semantic keybindings (ctrl-e for edit)
3. Provide helpful --header messages
4. Use --multi for batch operations
5. Toggle preview with ctrl-/ for large files
### Integration
```bash
# Combine with xargs
fd -e py | fzf -m | xargs wc -l
# Combine with while read
fd | fzf -m | while read file; do
echo "Processing: $file"
# Process file
done
# Combine with command substitution
vim $(fzf)
cd $(fd -t d | fzf)
```
### Scripting
```bash
# Exit code check
if result=$(fd | fzf); then
echo "Selected: $result"
else
echo "No selection or cancelled"
exit 1
fi
# Multi-select results
selected=$(fd | fzf -m)
if [[ -n "$selected" ]]; then
echo "$selected" | while read file; do
process "$file"
done
fi
```

656
references/jq-cookbook.md Normal file
View File

@@ -0,0 +1,656 @@
# jq - JSON Processing Cookbook
## Overview
`jq` is a lightweight command-line JSON processor. It's like `sed` for JSON data.
## Basic Queries
### Identity and Pretty Print
```bash
# Identity (pretty print)
jq . file.json
# Compact output
jq -c . file.json
# Raw output (no quotes for strings)
jq -r . file.json
# Sort keys
jq -S . file.json
```
### Accessing Fields
```bash
# Top-level field
jq '.name' file.json
# Nested field
jq '.user.email' file.json
# Deeply nested
jq '.data.user.profile.settings.theme' file.json
# Optional fields (null if missing)
jq '.user.address?' file.json
```
### Array Access
```bash
# First element
jq '.[0]' file.json
# Last element
jq '.[-1]' file.json
# Specific index
jq '.[2]' file.json
# Slice (first 3 elements)
jq '.[:3]' file.json
# Slice (elements 2-5)
jq '.[2:5]' file.json
# All elements
jq '.[]' file.json
```
## Iterating
### Array Iteration
```bash
# Extract field from each element
jq '.users[].name' file.json
# Multiple fields
jq '.users[] | .name, .email' file.json
# Nested iteration
jq '.departments[].employees[].name' file.json
```
### Object Iteration
```bash
# All values
jq '.[]' file.json
# All keys
jq 'keys' file.json
# Key-value pairs
jq 'to_entries' file.json
# Iterate key-value
jq 'to_entries[] | .key + ": " + (.value | tostring)' file.json
```
## Filtering
### Select
```bash
# Filter by condition
jq '.users[] | select(.active == true)' file.json
# Multiple conditions (AND)
jq '.users[] | select(.active == true and .age > 18)' file.json
# Multiple conditions (OR)
jq '.users[] | select(.active == true or .admin == true)' file.json
# Not
jq '.users[] | select(.active != false)' file.json
# Check field exists
jq '.users[] | select(.email != null)' file.json
jq '.users[] | select(has("email"))' file.json
```
### Comparison Operators
```bash
# Equality
jq '.items[] | select(.status == "active")' file.json
# Greater/Less than
jq '.items[] | select(.price > 100)' file.json
jq '.items[] | select(.price <= 50)' file.json
# String contains
jq '.items[] | select(.name | contains("widget"))' file.json
# String starts with
jq '.items[] | select(.name | startswith("A"))' file.json
# String ends with
jq '.items[] | select(.name | endswith(".json"))' file.json
# Regex match
jq '.items[] | select(.email | test("@gmail\\.com$"))' file.json
```
## Transforming
### Map
```bash
# Extract single field
jq '.users | map(.name)' file.json
# Extract multiple fields
jq '.users | map({name, email})' file.json
# Transform values
jq '.prices | map(. * 1.1)' file.json
# Conditional transformation
jq '.users | map(if .active then .name else empty end)' file.json
```
### Reduce
```bash
# Sum
jq '[.items[].price] | add' file.json
# Average
jq '[.items[].price] | add / length' file.json
# Min/Max
jq '[.items[].price] | min' file.json
jq '[.items[].price] | max' file.json
# Count
jq '.items | length' file.json
# Custom reduce
jq 'reduce .items[] as $item (0; . + $item.price)' file.json
```
### Group By
```bash
# Group by field
jq 'group_by(.category)' file.json
# Group and count
jq 'group_by(.category) | map({category: .[0].category, count: length})' file.json
# Group and aggregate
jq 'group_by(.category) | map({
category: .[0].category,
total: map(.price) | add
})' file.json
```
## Sorting
```bash
# Sort array
jq 'sort' file.json
# Sort by field
jq 'sort_by(.name)' file.json
# Reverse sort
jq 'sort_by(.name) | reverse' file.json
# Sort by multiple fields
jq 'sort_by(.category, .name)' file.json
# Sort numbers
jq 'sort_by(.price | tonumber)' file.json
```
## Constructing
### Objects
```bash
# New object with specific fields
jq '{name: .name, email: .email}' file.json
# Rename fields
jq '{username: .name, mail: .email}' file.json
# Computed fields
jq '{
name: .name,
fullName: .firstName + " " + .lastName,
discountPrice: .price * 0.9
}' file.json
# Nested objects
jq '{
user: {
name: .name,
contact: {email: .email}
}
}' file.json
```
### Arrays
```bash
# Create array from fields
jq '[.name, .email, .age]' file.json
# Array of objects
jq '[.users[] | {name, email}]' file.json
# Flatten nested arrays
jq '.departments[].employees | flatten' file.json
# Unique elements
jq '.tags | unique' file.json
# Array difference
jq '.a - .b' file.json
```
## String Operations
```bash
# Concatenation
jq '.firstName + " " + .lastName' file.json
# Interpolation
jq '"Hello, \(.name)!"' file.json
# String length
jq '.name | length' file.json
# Uppercase/Lowercase
jq '.name | ascii_upcase' file.json
jq '.name | ascii_downcase' file.json
# Split string
jq '.path | split("/")' file.json
# Join array
jq '.tags | join(", ")' file.json
# Trim whitespace
jq '.text | gsub("^\\s+|\\s+$"; "")' file.json
# Replace
jq '.text | gsub("old"; "new")' file.json
```
## Type Conversions
```bash
# To string
jq '.age | tostring' file.json
# To number
jq '.price | tonumber' file.json
# To array
jq '[.]' file.json
# Type check
jq '.value | type' file.json
# Type test
jq 'if .value | type == "string" then "is string" else "not string" end' file.json
```
## Conditionals
```bash
# If-then-else
jq 'if .active then "active" else "inactive" end' file.json
# Multiple conditions
jq 'if .score >= 90 then "A"
elif .score >= 80 then "B"
elif .score >= 70 then "C"
else "F"
end' file.json
# Ternary-like
jq '.users[] | {name, status: (if .active then "active" else "inactive" end)}' file.json
# Alternative operator
jq '.value // "default"' file.json # Use "default" if .value is null/false
```
## Combining Data
### Merging
```bash
# Merge objects
jq '. + {newField: "value"}' file.json
# Merge arrays
jq '.a + .b' file.json
# Recursive merge
jq '. * {user: {active: true}}' file.json
# Merge multiple files
jq -s '.[0] + .[1]' file1.json file2.json
```
### Joining
```bash
# Array join
jq '.array1 + .array2' file.json
# Unique elements from multiple arrays
jq '(.array1 + .array2) | unique' file.json
# Intersection
jq '.array1 - (.array1 - .array2)' file.json
```
## Advanced Patterns
### Working with Nested Data
```bash
# Recursive descent
jq '.. | .name? // empty' file.json
# Find all values for a key
jq '.. | .email? // empty' file.json
# Flatten deeply nested structure
jq '[.. | .users? // empty] | flatten' file.json
```
### Multiple Outputs
```bash
# Multiple queries
jq '.name, .email' file.json
# Conditional multiple outputs
jq '.users[] | .name, (if .admin then "ADMIN" else empty end)' file.json
```
### Error Handling
```bash
# Try-catch
jq 'try .field catch "not found"' file.json
# Optional field access
jq '.user?.email?' file.json
# Default values
jq '.count // 0' file.json
```
### Custom Functions
```bash
# Define and use function
jq 'def double: . * 2; .values[] | double' file.json
# Function with parameters
jq 'def multiply(n): . * n; .values[] | multiply(3)' file.json
# Recursive function
jq 'def factorial: if . <= 1 then 1 else . * ((. - 1) | factorial) end; 5 | factorial'
```
## Practical Examples
### API Response Processing
```bash
# Extract IDs
curl -s 'https://api.example.com/users' | jq '.data[].id'
# Format for CSV
curl -s 'https://api.example.com/users' | \
jq -r '.data[] | [.id, .name, .email] | @csv'
# Extract nested data
curl -s 'https://api.example.com/posts' | \
jq '.posts[] | {
id,
title,
author: .user.name,
comments: .comments | length
}'
```
### Configuration Management
```bash
# Update config value
jq '.database.host = "localhost"' config.json
# Add new field
jq '. + {newFeature: true}' config.json
# Remove field
jq 'del(.deprecated)' config.json
# Merge configs
jq -s '.[0] * .[1]' base.json override.json > merged.json
```
### Data Analysis
```bash
# Count by category
jq 'group_by(.category) | map({category: .[0].category, count: length})' data.json
# Top 10 by price
jq 'sort_by(.price) | reverse | .[:10]' data.json
# Statistics
jq '{
total: length,
avgPrice: (map(.price) | add / length),
maxPrice: (map(.price) | max),
minPrice: (map(.price) | min)
}' data.json
```
### Testing and Validation
```bash
# Check required fields
jq '.users[] | select(has("name") and has("email") | not)' data.json
# Validate email format
jq '.users[] | select(.email | test("^[^@]+@[^@]+$") | not)' data.json
# Find duplicates
jq 'group_by(.id) | map(select(length > 1))' data.json
```
### Log Processing
```bash
# Filter log level
jq 'select(.level == "error")' logs.json
# Extract error messages
jq 'select(.level == "error") | .message' logs.json
# Group errors by type
jq -s 'group_by(.error_type) | map({type: .[0].error_type, count: length})' logs.json
# Time range filter
jq 'select(.timestamp >= "2024-01-01" and .timestamp < "2024-02-01")' logs.json
```
## Output Formats
```bash
# Raw output (no quotes)
jq -r '.name' file.json
# Compact output
jq -c '.' file.json
# Tab-separated
jq -r '.[] | [.name, .age] | @tsv' file.json
# CSV
jq -r '.[] | [.name, .age, .email] | @csv' file.json
# URL encoding
jq -r '.params | @uri' file.json
# Base64 encoding
jq -r '.data | @base64' file.json
# HTML encoding
jq -r '.content | @html' file.json
# JSON output (default)
jq '.' file.json
```
## Command-Line Options
```bash
# Read from stdin
echo '{"name":"test"}' | jq .
# Multiple files
jq . file1.json file2.json
# Slurp (read all files into array)
jq -s . file1.json file2.json
# Null input (generate data)
jq -n '{name: "test", date: now}'
# Exit with status code
jq -e 'select(.status == "ok")' file.json
# Tab indentation
jq --tab . file.json
# No color
jq -M . file.json
# Color (force)
jq -C . file.json
```
## Tips and Tricks
### Debugging
```bash
# Pretty print
jq . file.json | less
# Inspect structure
jq 'paths' file.json
# Show all keys
jq '[paths | select(length == 1)] | unique' file.json
# Debug filter
jq 'debug | .name' file.json
```
### Performance
```bash
# Stream processing (memory efficient)
jq --stream . large.json
# Compact input
jq -c . file.json
# Limit output
jq 'limit(10; .items[])' file.json
```
### Combining with Other Tools
```bash
# With curl
curl -s 'https://api.github.com/users/github' | jq '.name'
# With fd/rg
fd -e json | xargs jq '.version'
# With fzf
jq -r '.users[].name' users.json | fzf
# With xargs
jq -r '.files[]' manifest.json | xargs cat
```
## Common Patterns
### Filter-Map-Reduce
```bash
# Classic pattern
jq '.items[] | # iterate
select(.active) | # filter
.price | # map
add' # reduce
```
### Pagination
```bash
# Page 1 (items 0-9)
jq '.items[0:10]' data.json
# Page 2 (items 10-19)
jq '.items[10:20]' data.json
# Function for pagination
page() {
local page=$1
local size=${2:-10}
local start=$((page * size))
local end=$((start + size))
jq ".items[$start:$end]" data.json
}
```
### Data Migration
```bash
# Old format to new format
jq 'map({
id: .user_id,
profile: {
name: .username,
email: .user_email
}
})' old_format.json > new_format.json
```
## Error Messages
```bash
# Common errors and fixes
# "parse error: Invalid numeric literal"
# → Check JSON syntax, trailing commas
# "Cannot iterate over null"
# → Add optional operator: .field[]?
# "Cannot index string with string"
# → Check data types, .field might be string not object
# "jq: error: syntax error"
# → Check quote escaping in shell
```

443
references/rg-patterns.md Normal file
View File

@@ -0,0 +1,443 @@
# rg - Ripgrep Patterns and Usage
## Overview
`ripgrep` (rg) is an extremely fast text search tool that respects `.gitignore` and supports regex patterns.
## Basic Search
```bash
# Simple search
rg 'pattern'
# Case-insensitive
rg -i 'pattern'
# Case-sensitive (force)
rg -s 'Pattern'
# Smart case (case-insensitive unless uppercase present)
rg -S 'pattern'
```
## File Type Filtering
```bash
# Search in specific file types
rg -t py 'import' # Python files
rg -t rust 'fn main' # Rust files
rg -t js 'function' # JavaScript files
# Multiple types
rg -t py -t js 'class'
# Exclude types
rg -T py 'pattern' # Everything except Python
# List available types
rg --type-list
```
## Context Control
```bash
# Lines before match
rg -B 2 'pattern'
# Lines after match
rg -A 5 'pattern'
# Lines before and after
rg -C 3 'pattern'
# Both (different values)
rg -B 2 -A 5 'pattern'
```
## Output Formatting
```bash
# Show line numbers (default in terminal)
rg -n 'pattern'
# Hide line numbers
rg -N 'pattern'
# Show column numbers
rg --column 'pattern'
# Show file paths only
rg -l 'pattern'
# Show files without matches
rg --files-without-match 'pattern'
# Count matches per file
rg -c 'pattern'
# Count total matches
rg --count-matches 'pattern'
```
## Advanced Search
### Word Boundaries
```bash
# Match whole words only
rg -w 'word'
# Example: find 'test' but not 'testing'
rg -w 'test'
# Regex word boundaries
rg '\bword\b'
```
### Multiline Search
```bash
# Enable multiline mode
rg -U 'pattern.*spanning.*lines'
# Example: find function definitions
rg -U 'def \w+\([^)]*\):\s*"""[^"]*"""'
# Multiline with context
rg -U -C 2 'pattern.*multiline'
```
### Fixed Strings
```bash
# Literal string search (no regex, faster)
rg -F 'literal.string[with]special.chars'
# Case-insensitive literal
rg -F -i 'Literal String'
```
## File Filtering
### By Path
```bash
# Search in specific directory
rg 'pattern' src/
# Multiple directories
rg 'pattern' src/ tests/
# Glob patterns
rg -g '*.py' 'pattern'
rg -g '**/*test*.py' 'pattern'
# Multiple globs
rg -g '*.{js,ts}' 'pattern'
# Negative globs (exclude)
rg -g '!*test*' 'pattern'
rg -g '!*.min.js' 'pattern'
```
### Hidden and Ignored Files
```bash
# Search hidden files
rg --hidden 'pattern'
# Ignore .gitignore rules
rg --no-ignore 'pattern'
# Both
rg --hidden --no-ignore 'pattern'
# Search specific ignored directories
rg --no-ignore -g 'node_modules/**' 'pattern'
```
## Regex Patterns
### Basic Regex
```bash
# Any character
rg 'a.c' # matches 'abc', 'a2c', etc.
# Character classes
rg '[0-9]+' # one or more digits
rg '[a-zA-Z]+' # letters only
rg '[^0-9]' # anything except digits
# Anchors
rg '^pattern' # start of line
rg 'pattern$' # end of line
rg '^pattern$' # entire line matches
```
### Quantifiers
```bash
# Zero or more
rg 'ab*c' # ac, abc, abbc, etc.
# One or more
rg 'ab+c' # abc, abbc, etc.
# Zero or one
rg 'ab?c' # ac, abc
# Exact count
rg 'a{3}' # aaa
rg 'a{2,4}' # aa, aaa, aaaa
rg 'a{2,}' # aa, aaa, aaaa, etc.
```
### Groups and Alternation
```bash
# Groups
rg '(ab)+' # ab, abab, ababab, etc.
# Alternation
rg 'cat|dog' # cat or dog
rg '(import|from) numpy'
# Non-capturing groups
rg '(?:http|https)://[^\s]+'
```
### Lookahead/Lookbehind
```bash
# Positive lookahead
rg 'test(?=_unit)' # 'test' followed by '_unit'
# Negative lookahead
rg 'test(?!_integration)' # 'test' not followed by '_integration'
# Positive lookbehind
rg '(?<=def )\\w+' # word after 'def '
# Negative lookbehind
rg '(?<!@)property' # 'property' not preceded by '@'
```
## Replacement (with sed/perl)
```bash
# Find and replace (dry run)
rg -l 'old_pattern' | xargs sed -n 's/old_pattern/new_pattern/gp'
# Find and replace (in-place)
rg -l 'old_pattern' | xargs sed -i '' 's/old_pattern/new_pattern/g'
# Using perl for complex replacements
rg -l 'pattern' | xargs perl -pi -e 's/old/new/g'
```
## JSON Output
```bash
# JSON output for scripting
rg --json 'pattern'
# Parse with jq
rg --json 'pattern' | jq -r 'select(.type == "match") | .data.path.text'
# Get line numbers
rg --json 'pattern' | jq -r 'select(.type == "match") | "\(.data.path.text):\(.data.line_number)"'
```
## Statistics and Analysis
```bash
# Show statistics
rg --stats 'pattern'
# Count matches per file
rg -c 'pattern' | sort -t: -k2 -nr
# Find most common matches
rg -o 'pattern' | sort | uniq -c | sort -nr
# Files with most matches
rg -c 'TODO' | awk -F: '$2 > 0' | sort -t: -k2 -nr | head
```
## Common Workflows
### Code Search
```bash
# Find function definitions (Python)
rg 'def \w+\(' -t py
# Find class definitions
rg 'class \w+' -t py
# Find imports
rg 'from .* import' -t py
# Find TODO/FIXME comments
rg '(TODO|FIXME|XXX|HACK):'
# Find console.log statements
rg 'console\.(log|error|warn)' -t js
```
### Configuration Search
```bash
# Find API keys (be careful!)
rg -i 'api[_-]?key' -g '!*.md'
# Find environment variables
rg 'process\.env\.' -t js
rg 'os\.getenv' -t py
# Find hardcoded IPs
rg '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'
# Find URLs
rg 'https?://[^\s<>"{}|\\^`\[\]]+'
```
### Dependency Analysis
```bash
# Find all imports of a module
rg 'from mymodule import' -t py
# Find all requires
rg "require\(['\"].*['\"]\\)" -t js
# Find external dependencies
rg 'import.*from ["\'](?!\.)[^"\']+' -t py
```
### Testing
```bash
# Find test functions
rg 'def test_\w+' -t py
rg 'it\(["\']' -t js
# Find assertions
rg 'assert\w*\(' -t py
rg 'expect\(' -t js
# Find skipped tests
rg '@skip|@pytest\.mark\.skip' -t py
rg '\.skip\(' -t js
```
## Performance Tips
1. **Use file type filters**: `-t` is faster than glob patterns
2. **Use fixed strings when possible**: `-F` bypasses regex engine
3. **Limit search scope**: Specify directories explicitly
4. **Use word boundaries**: `-w` is faster than `\b` regex
5. **Respect .gitignore**: Default behavior is already optimized
## Interactive Workflows
```bash
# Search and edit
rg -l 'TODO' | fzf | xargs $EDITOR
# Search with preview
rg --line-number 'pattern' | fzf --delimiter : --preview 'bat --color=always {1} --highlight-line {2}'
# Multi-select and edit
rg -l 'pattern' | fzf -m | xargs $EDITOR
# Search, select, and replace
FILE=$(rg -l 'pattern' | fzf)
rg 'pattern' "$FILE" | fzf
```
## Configuration
Create `~/.ripgreprc` for default options:
```bash
# Always show line numbers
--line-number
# Smart case search
--smart-case
# Follow symlinks
--follow
# Custom type definitions
--type-add=web:*.{html,css,js}*
```
Use with: `export RIPGREP_CONFIG_PATH=~/.ripgreprc`
## Comparison with grep
| Task | grep | rg |
|------|------|-----|
| Case-insensitive | `grep -i` | `rg -i` |
| Recursive | `grep -r` | `rg` (default) |
| Show line numbers | `grep -n` | `rg -n` (default) |
| Context | `grep -C 3` | `rg -C 3` |
| File type | `grep --include='*.py'` | `rg -t py` |
| Exclude pattern | `grep --exclude='test*'` | `rg -g '!test*'` |
## Tips and Tricks
### Shell Aliases
```bash
alias rgi='rg -i' # Case-insensitive
alias rga='rg --hidden --no-ignore' # Search everything
alias rgl='rg -l' # Files only
alias rgc='rg -c' # Count per file
```
### Functions
```bash
# Search and edit function
rge() {
rg -l "$1" | fzf --preview "rg --color=always -C 5 '$1' {}" | xargs $EDITOR
}
# Search in specific file type
rgt() {
TYPE=$1
shift
rg -t "$TYPE" "$@"
}
# Search with context and open
rgf() {
rg --line-number "$1" | fzf --delimiter : --preview 'bat --color=always {1} --highlight-line {2}' | awk -F: '{print $1}' | xargs $EDITOR
}
```
### Complex Patterns
```bash
# Email addresses
rg '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
# Phone numbers (US)
rg '\b\d{3}[-.]?\d{3}[-.]?\d{4}\b'
# Hex colors
rg '#[0-9a-fA-F]{6}\b'
# Semantic versions
rg '\b\d+\.\d+\.\d+\b'
# Python function with type hints
rg 'def \w+\([^)]*\) -> \w+:'
# SQL queries in code
rg -U 'SELECT.*FROM.*WHERE'
```

677
references/yq-examples.md Normal file
View File

@@ -0,0 +1,677 @@
# yq - YAML/XML Processing Examples
## Overview
`yq` is a lightweight command-line YAML/XML processor. It's like `jq` but for YAML and XML.
## YAML Processing
### Basic Queries
```bash
# Pretty print
yq . file.yaml
# Access field
yq '.name' file.yaml
# Nested access
yq '.database.host' file.yaml
# Array access
yq '.items[0]' file.yaml
# All array elements
yq '.items[]' file.yaml
```
### Filtering
```bash
# Filter by condition
yq '.users[] | select(.active == true)' file.yaml
# Multiple conditions
yq '.users[] | select(.age > 18 and .active == true)' file.yaml
# String contains
yq '.items[] | select(.name | contains("widget"))' file.yaml
```
### Transforming
```bash
# Extract specific fields
yq '.users[] | {name, email}' file.yaml
# Map array
yq '.items | map(.price * 1.1)' file.yaml
# Sort
yq 'sort_by(.name)' file.yaml
# Group by
yq 'group_by(.category)' file.yaml
```
### Updating Values
```bash
# Update single field
yq '.database.host = "localhost"' file.yaml
# Update nested field
yq '.server.config.port = 8080' file.yaml
# Update array element
yq '.items[0].price = 99.99' file.yaml
# Add new field
yq '. + {"newField": "value"}' file.yaml
# Delete field
yq 'del(.deprecated)' file.yaml
```
### In-Place Editing
```bash
# Edit file in-place
yq -i '.version = "2.0"' file.yaml
# Multiple updates
yq -i '
.version = "2.0" |
.updated = now |
.deprecated = null
' file.yaml
```
### Merging Files
```bash
# Merge two YAML files
yq '. *= load("other.yaml")' base.yaml
# Merge with override
yq 'load("base.yaml") * load("override.yaml")'
# Merge multiple files
yq eval-all '. as $item ireduce ({}; . * $item)' file1.yaml file2.yaml file3.yaml
```
## Format Conversion
### YAML to JSON
```bash
# Convert YAML to JSON
yq -o json . file.yaml
# Compact JSON
yq -o json -I 0 . file.yaml
# Pretty JSON
yq -o json . file.yaml | jq .
```
### JSON to YAML
```bash
# Convert JSON to YAML
yq -P . file.json
# From stdin
echo '{"name":"test"}' | yq -P .
# Pipeline
cat file.json | yq -P . > file.yaml
```
### YAML to TOML
```bash
# Convert YAML to TOML
yq -o toml . file.yaml
```
### YAML to XML
```bash
# Convert YAML to XML
yq -o xml . file.yaml
```
## XML Processing
### Reading XML
```bash
# Parse XML
yq -p xml . file.xml
# Access element
yq -p xml '.root.element' file.xml
# Access attribute
yq -p xml '.root.+@attr' file.xml
# All elements
yq -p xml '.root.items[]' file.xml
```
### Filtering XML
```bash
# Filter by attribute
yq -p xml '.root.item[] | select(.+@id == "123")' file.xml
# Filter by element value
yq -p xml '.root.item[] | select(.name == "test")' file.xml
```
### XML to JSON
```bash
# Convert XML to JSON
yq -p xml -o json . file.xml
# Extract and convert
yq -p xml -o json '.root.items' file.xml
```
### XML to YAML
```bash
# Convert XML to YAML
yq -p xml . file.xml
# Pretty print
yq -p xml -P . file.xml
```
## Common Use Cases
### Docker Compose
```bash
# Get service names
yq '.services | keys' docker-compose.yml
# Get image for service
yq '.services.web.image' docker-compose.yml
# Update service port
yq -i '.services.web.ports[0] = "8080:80"' docker-compose.yml
# Add environment variable
yq -i '.services.web.environment.DEBUG = "true"' docker-compose.yml
# Get all exposed ports
yq '.services[].ports[]' docker-compose.yml
```
### Kubernetes
```bash
# Get pod name
yq '.metadata.name' pod.yaml
# Get container image
yq '.spec.containers[0].image' pod.yaml
# Update replicas
yq -i '.spec.replicas = 3' deployment.yaml
# Get all container names
yq '.spec.template.spec.containers[].name' deployment.yaml
# Get resource limits
yq '.spec.template.spec.containers[0].resources.limits' deployment.yaml
```
### CI/CD (GitHub Actions, GitLab CI)
```bash
# Get job names
yq '.jobs | keys' .github/workflows/ci.yml
# Get steps for job
yq '.jobs.build.steps[].name' .github/workflows/ci.yml
# Update trigger
yq -i '.on.push.branches = ["main", "develop"]' .github/workflows/ci.yml
# Get all used actions
yq '.jobs[].steps[].uses' .github/workflows/ci.yml
```
### Configuration Files
```bash
# Application config
yq '.app.port' config.yaml
yq '.database.connection.host' config.yaml
# Update config
yq -i '.app.debug = true' config.yaml
yq -i '.cache.enabled = false' config.yaml
# Merge configs
yq '. *= load("config.local.yaml")' config.yaml
```
### Ansible
```bash
# Get hosts
yq '.all.hosts | keys' inventory.yaml
# Get variables
yq '.all.vars' inventory.yaml
# Update host variable
yq -i '.all.hosts.server1.ansible_host = "192.168.1.10"' inventory.yaml
# Get all playbook tasks
yq '.[] | select(.tasks) | .tasks[].name' playbook.yaml
```
## Advanced Patterns
### Conditional Updates
```bash
# Update if exists
yq '(select(.field) | .field) = "new-value"' file.yaml
# Update based on condition
yq '(.items[] | select(.price > 100) | .discount) = 0.2' file.yaml
# Add field if missing
yq '. + (if has("field") then {} else {"field": "default"} end)' file.yaml
```
### Complex Transformations
```bash
# Restructure data
yq '.users | map({
username: .name,
contact: {
email: .email,
phone: .phone
}
})' file.yaml
# Aggregate data
yq '.items | group_by(.category) | map({
category: .[0].category,
total: map(.price) | add
})' file.yaml
# Pivot data
yq 'reduce .items[] as $item ({}; .[$item.name] = $item.value)' file.yaml
```
### Working with Arrays
```bash
# Append to array
yq '.items += ["new-item"]' file.yaml
# Prepend to array
yq '.items = ["new-item"] + .items' file.yaml
# Remove from array
yq 'del(.items[2])' file.yaml
# Filter array
yq '.items = [.items[] | select(.active == true)]' file.yaml
# Unique array
yq '.items | unique' file.yaml
# Flatten array
yq '.items | flatten' file.yaml
```
### Multi-Document YAML
```bash
# Process all documents
yq '.' multi-doc.yaml
# Select specific document
yq 'select(document_index == 0)' multi-doc.yaml
# Filter documents
yq 'select(.kind == "Deployment")' multi-doc.yaml
# Update all documents
yq -i '.metadata.labels.app = "myapp"' multi-doc.yaml
```
## String Operations
```bash
# Concatenation
yq '.fullName = .firstName + " " + .lastName' file.yaml
# Interpolation
yq '.message = "Hello, " + .name + "!"' file.yaml
# Uppercase
yq '.name | upcase' file.yaml
# Lowercase
yq '.name | downcase' file.yaml
# Split
yq '.path | split("/")' file.yaml
# Join
yq '.items | join(", ")' file.yaml
# Replace
yq '.text | sub("old", "new")' file.yaml
# Trim
yq '.text | trim' file.yaml
```
## Type Checking and Conversion
```bash
# Check type
yq '.field | type' file.yaml
# Convert to string
yq '.number | tostring' file.yaml
# Convert to number
yq '.string | tonumber' file.yaml
# Check if exists
yq 'has("field")' file.yaml
# Length
yq '.items | length' file.yaml
```
## Comments
```bash
# Preserve comments (default behavior)
yq '.field = "new-value"' file.yaml
# Add headComment
yq '.field headComment="This is a comment"' file.yaml
# Add lineComment
yq '.field lineComment="inline comment"' file.yaml
# Add footComment
yq '.field footComment="bottom comment"' file.yaml
```
## Anchors and Aliases
```bash
# Create anchor
yq '.base.&anchor = {"key": "value"}' file.yaml
# Reference anchor
yq '.other = .base.*anchor' file.yaml
# Merge anchors
yq '.combined = .base.*anchor * .override' file.yaml
```
## Validation
```bash
# Check if valid YAML
yq . file.yaml > /dev/null && echo "Valid" || echo "Invalid"
# Validate schema (requires schema file)
yq 'load("schema.yaml") | . as $schema | load("data.yaml") | validate($schema)'
# Check required fields
yq 'select(has("requiredField") | not)' file.yaml
# Validate format
yq 'select(.email | test("^[^@]+@[^@]+$") | not)' file.yaml
```
## Output Formatting
```bash
# Default YAML output
yq . file.yaml
# Compact (minimal whitespace)
yq -I 0 . file.yaml
# Custom indentation (4 spaces)
yq -I 4 . file.yaml
# No colors
yq -C 0 . file.yaml
# Force colors
yq -C 1 . file.yaml
# Raw output (no quotes)
yq -r '.name' file.yaml
# One line per document
yq -o json . multi-doc.yaml
```
## Scripting Examples
### Environment-Specific Configs
```bash
#!/bin/bash
ENV=${1:-dev}
yq ". *= load(\"config.$ENV.yaml\")" config.base.yaml > config.yaml
```
### Bulk Updates
```bash
#!/bin/bash
for file in *.yaml; do
yq -i '.metadata.labels.version = "2.0"' "$file"
done
```
### Config Validation
```bash
#!/bin/bash
REQUIRED_FIELDS=("name" "version" "description")
for field in "${REQUIRED_FIELDS[@]}"; do
if ! yq -e "has(\"$field\")" config.yaml > /dev/null; then
echo "Missing required field: $field"
exit 1
fi
done
```
### Secret Management
```bash
#!/bin/bash
# Extract secrets from YAML
yq '.secrets' config.yaml > secrets.yaml
# Encrypt secrets (example with age)
yq '.secrets' config.yaml | age -e -o secrets.age
# Decrypt and merge
age -d secrets.age | yq -P . > secrets.yaml
yq '. *= load("secrets.yaml")' config.yaml
```
## Integration Examples
### With Git
```bash
# Pre-commit hook to sort keys
git diff --cached --name-only | \
grep '\.yaml$' | \
xargs -I {} yq -i -P 'sort_keys(..)' {}
```
### With Docker
```bash
# Extract image tags
yq '.services[].image' docker-compose.yml | \
cut -d: -f2 | \
sort -u
# Update all images to latest
yq -i '(.services[].image) |= sub(":.*", ":latest")' docker-compose.yml
```
### With Kubernetes
```bash
# Apply with modifications
yq '.spec.replicas = 3' deployment.yaml | kubectl apply -f -
# Extract all images
kubectl get pods -o yaml | \
yq '.items[].spec.containers[].image' | \
sort -u
# Generate manifests
yq eval-all '. as $item ireduce ({}; . * $item)' \
base/*.yaml overlays/prod/*.yaml | \
kubectl apply -f -
```
### With CI/CD
```bash
# Update version in workflow
VERSION=$(git describe --tags)
yq -i ".env.VERSION = \"$VERSION\"" .github/workflows/deploy.yml
# Extract job matrix
MATRIX=$(yq -o json '.jobs.test.strategy.matrix' .github/workflows/ci.yml)
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
```
## Performance Tips
1. **Use in-place editing**: `-i` avoids reading/writing twice
2. **Pipe instead of temp files**: More efficient for transformations
3. **Specific paths**: Access `.field.subfield` instead of iterating
4. **Limit output**: Use `select` early to filter data
5. **Batch operations**: Combine multiple updates in one command
## Common Patterns
### Configuration Merging
```bash
# Base + environment + local
yq eval-all '. as $item ireduce ({}; . * $item)' \
config.base.yaml \
config.$ENV.yaml \
config.local.yaml
```
### Secret Injection
```bash
# Replace placeholders with actual values
yq '(.. | select(tag == "!!str")) |= envsubst' config.yaml
```
### Manifest Generation
```bash
# Generate K8s manifests from template
yq '(.metadata.name) = "app-" + env(ENV) |
(.spec.replicas) = env(REPLICAS) |
(.spec.template.spec.containers[0].image) = env(IMAGE)' \
template.yaml
```
## Tips and Tricks
### Debugging
```bash
# Pretty print to see structure
yq -P . file.yaml
# Show paths
yq '.. | path | join(".")' file.yaml
# Find all keys
yq '.. | select(tag == "!!map") | keys' file.yaml
```
### Working with Large Files
```bash
# Stream processing
yq -N . large.yaml # Process without loading entirely
# Extract specific document
yq 'select(document_index == 5)' large-multi-doc.yaml
# Count documents
yq -N '.' multi-doc.yaml | grep -c '^---$'
```
### Shell Aliases
```bash
alias yqp='yq -P' # Pretty print
alias yqj='yq -o json' # To JSON
alias yqs='yq -I 2' # 2-space indent
alias yqc='yq -C 1' # Force colors
```
## Error Handling
```bash
# Check if file exists and is valid
if yq . file.yaml &>/dev/null; then
echo "Valid YAML"
else
echo "Invalid YAML or file not found"
exit 1
fi
# Try with fallback
VALUE=$(yq '.field // "default"' file.yaml)
# Conditional processing
if yq -e '.enabled == true' config.yaml > /dev/null; then
# Process when enabled
yq '.items[]' config.yaml
fi
```
## Comparison with Other Tools
| Task | yq | jq (JSON) | sed/awk |
|------|----|-----------| --------|
| Parse YAML | Native | Need conversion | Complex |
| Parse JSON | Can do | Native | Possible |
| Update in-place | `-i` flag | Needs sponge | `-i` flag |
| Preserve comments | Yes | N/A | Yes |
| Type safety | Yes | Yes | No |
| Learning curve | Medium | Medium | Low/High |

278
scripts/combo-search.sh Executable file
View File

@@ -0,0 +1,278 @@
#!/usr/bin/env bash
#
# combo-search.sh - Example combination workflows using CLI tools
#
# This script demonstrates common patterns for combining fd, rg, ast-grep,
# fzf, jq, and yq to solve real-world repository navigation tasks.
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Helper function to print section headers
print_section() {
echo -e "\n${BLUE}=== $1 ===${NC}\n"
}
# Helper function to print commands before execution
print_command() {
echo -e "${YELLOW}$ $1${NC}"
}
# 1. Find Python files with TODO comments, select one, and edit
find_and_edit_todos() {
print_section "Find and Edit TODOs"
print_command "rg -l 'TODO' -t py | fzf --preview 'rg -C 3 --color=always TODO {}' | xargs \$EDITOR"
local file
file=$(rg -l 'TODO' -t py | fzf --preview 'rg -C 3 --color=always TODO {}' || true)
if [[ -n "$file" ]]; then
echo -e "${GREEN}Selected: $file${NC}"
# Uncomment to actually open editor:
# $EDITOR "$file"
else
echo -e "${RED}No file selected${NC}"
fi
}
# 2. Find function definitions, select one, and show its usages
find_function_usages() {
print_section "Find Function and Its Usages"
local func_pattern="def $1(\$\$\$):"
print_command "ast-grep --pattern '$func_pattern' -l py | fzf"
local result
result=$(ast-grep --pattern "$func_pattern" -l py | fzf --preview 'bat --color=always {1}' || true)
if [[ -n "$result" ]]; then
local func_name
func_name=$(echo "$result" | sed -n 's/.*def \([^(]*\)(.*/\1/p')
echo -e "${GREEN}Function: $func_name${NC}"
echo -e "${YELLOW}Usages:${NC}"
print_command "rg '\\b$func_name\\(' -t py"
rg "\b$func_name\(" -t py || echo "No usages found"
fi
}
# 3. Find configuration files and edit selected one
edit_config_file() {
print_section "Find and Edit Configuration File"
print_command "fd -e json -e yaml -e toml config | fzf --preview 'bat --color=always {}'"
local config
config=$(fd -e json -e yaml -e toml config | fzf --preview 'bat --color=always {}' || true)
if [[ -n "$config" ]]; then
echo -e "${GREEN}Selected: $config${NC}"
# Uncomment to actually open editor:
# $EDITOR "$config"
else
echo -e "${RED}No config selected${NC}"
fi
}
# 4. Search for imports of a module and show file list
find_module_imports() {
print_section "Find Module Imports"
local module="$1"
print_command "ast-grep --pattern 'from $module import \$\$\$' -l py"
print_command "ast-grep --pattern 'import $module' -l py"
echo -e "${YELLOW}Files importing $module:${NC}"
{
ast-grep --pattern "from $module import \$\$\$" -l py 2>/dev/null || true
ast-grep --pattern "import $module" -l py 2>/dev/null || true
} | sort -u
}
# 5. Find recently modified files and select for editing
edit_recent_file() {
print_section "Find and Edit Recently Modified File"
print_command "fd -t f --changed-within 7d | fzf --preview 'bat --color=always {}'"
local file
file=$(fd -t f --changed-within 7d | fzf --preview 'bat --color=always {}' || true)
if [[ -n "$file" ]]; then
echo -e "${GREEN}Selected: $file${NC}"
# Uncomment to actually open editor:
# $EDITOR "$file"
else
echo -e "${RED}No file selected${NC}"
fi
}
# 6. Find JSON files and explore with jq
explore_json_files() {
print_section "Find and Explore JSON Files"
print_command "fd -e json | fzf --preview 'jq --color-output . {}'"
local json_file
json_file=$(fd -e json | fzf --preview 'jq --color-output . {}' || true)
if [[ -n "$json_file" ]]; then
echo -e "${GREEN}Selected: $json_file${NC}"
echo -e "${YELLOW}Keys:${NC}"
jq 'keys' "$json_file"
else
echo -e "${RED}No JSON file selected${NC}"
fi
}
# 7. Find test files with failures
find_failing_tests() {
print_section "Find Test Files with 'FAIL' or 'ERROR'"
print_command "rg -l '(FAIL|ERROR)' tests/ -t py | fzf --preview 'rg -C 5 --color=always \"(FAIL|ERROR)\" {}'"
local test_file
test_file=$(rg -l '(FAIL|ERROR)' tests/ -t py 2>/dev/null | \
fzf --preview 'rg -C 5 --color=always "(FAIL|ERROR)" {}' || true)
if [[ -n "$test_file" ]]; then
echo -e "${GREEN}Selected: $test_file${NC}"
else
echo -e "${RED}No failing tests found or none selected${NC}"
fi
}
# 8. Find and count function definitions by file
count_functions_per_file() {
print_section "Count Function Definitions Per File"
print_command "ast-grep --pattern 'def \$FUNC(\$\$\$):' -l py | xargs -I {} sh -c 'echo \"{}: \$(ast-grep --pattern \"def \\\$FUNC(\\\$\\\$\\\$):\" {} | wc -l)\"'"
ast-grep --pattern 'def $FUNC($$$):' -l py 2>/dev/null | \
xargs -I {} sh -c 'echo "{}: $(ast-grep --pattern "def \$FUNC(\$\$\$):" {} | wc -l)"' | \
sort -t: -k2 -nr | \
head -10
}
# 9. Find all TODO/FIXME comments and group by file
list_todos_by_file() {
print_section "List TODOs/FIXMEs Grouped by File"
print_command "rg -n '(TODO|FIXME|XXX|HACK):' | sort | fzf --preview 'bat --color=always {1} --highlight-line {2}'"
rg -n '(TODO|FIXME|XXX|HACK):' 2>/dev/null | \
sort | \
fzf --preview 'bat --color=always {1} --highlight-line {2}' || true
}
# 10. Interactive refactoring: find pattern and preview replacement
interactive_refactor() {
print_section "Interactive Refactoring Preview"
local old_pattern="$1"
local new_pattern="$2"
print_command "ast-grep --pattern '$old_pattern' -l py | fzf -m --preview 'ast-grep --pattern \"$old_pattern\" {}'"
local files
files=$(ast-grep --pattern "$old_pattern" -l py 2>/dev/null | \
fzf -m --preview "ast-grep --pattern '$old_pattern' {}" || true)
if [[ -n "$files" ]]; then
echo -e "${GREEN}Selected files for refactoring:${NC}"
echo "$files"
echo -e "\n${YELLOW}To apply refactoring, run:${NC}"
echo -e "ast-grep --pattern '$old_pattern' --rewrite '$new_pattern' -l py --interactive"
else
echo -e "${RED}No files selected${NC}"
fi
}
# Main menu
show_menu() {
echo -e "\n${BLUE}CLI Ninja - Combination Workflows${NC}\n"
echo "1. Find and edit TODOs"
echo "2. Find function usages (requires function name)"
echo "3. Edit config file"
echo "4. Find module imports (requires module name)"
echo "5. Edit recently modified file"
echo "6. Explore JSON files"
echo "7. Find failing tests"
echo "8. Count functions per file"
echo "9. List TODOs/FIXMEs"
echo "10. Interactive refactoring preview (requires old/new patterns)"
echo "0. Exit"
echo
}
# Main execution
main() {
if [[ $# -eq 0 ]]; then
show_menu
read -rp "Select workflow: " choice
case $choice in
1) find_and_edit_todos ;;
2)
read -rp "Enter function name: " func
find_function_usages "$func"
;;
3) edit_config_file ;;
4)
read -rp "Enter module name: " module
find_module_imports "$module"
;;
5) edit_recent_file ;;
6) explore_json_files ;;
7) find_failing_tests ;;
8) count_functions_per_file ;;
9) list_todos_by_file ;;
10)
read -rp "Enter old pattern: " old
read -rp "Enter new pattern: " new
interactive_refactor "$old" "$new"
;;
0) exit 0 ;;
*) echo -e "${RED}Invalid choice${NC}" ;;
esac
else
# Direct command execution
case "$1" in
todos) find_and_edit_todos ;;
function)
[[ $# -ge 2 ]] || { echo "Usage: $0 function <name>"; exit 1; }
find_function_usages "$2"
;;
config) edit_config_file ;;
imports)
[[ $# -ge 2 ]] || { echo "Usage: $0 imports <module>"; exit 1; }
find_module_imports "$2"
;;
recent) edit_recent_file ;;
json) explore_json_files ;;
failing) find_failing_tests ;;
count) count_functions_per_file ;;
list-todos) list_todos_by_file ;;
refactor)
[[ $# -ge 3 ]] || { echo "Usage: $0 refactor <old> <new>"; exit 1; }
interactive_refactor "$2" "$3"
;;
*)
echo "Usage: $0 [command] [args]"
echo "Run without arguments for interactive menu"
exit 1
;;
esac
fi
}
main "$@"

View File

@@ -0,0 +1,436 @@
#!/usr/bin/env bash
#
# interactive-code-finder.sh - Interactive ast-grep + fzf workflow
#
# This script provides an interactive way to search for code patterns
# using ast-grep and select results with fzf for further action.
set -euo pipefail
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# Configuration
LANGUAGE="${LANGUAGE:-py}"
PREVIEW_LINES=20
# Helper functions
print_header() {
echo -e "\n${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BLUE} $1${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
print_info() {
echo -e "${CYAN} $1${NC}"
}
print_warning() {
echo -e "${YELLOW}$1${NC}"
}
# Check dependencies
check_dependencies() {
local missing=()
for cmd in ast-grep fzf bat; do
if ! command -v "$cmd" &> /dev/null; then
missing+=("$cmd")
fi
done
if [[ ${#missing[@]} -gt 0 ]]; then
print_error "Missing required tools: ${missing[*]}"
echo
echo "Install with:"
echo " brew install ast-grep fzf bat"
exit 1
fi
}
# Language-specific pattern examples
get_pattern_examples() {
local lang="$1"
case "$lang" in
py|python)
cat <<EOF
Function definitions: def \$FUNC(\$\$\$):
Class definitions: class \$CLASS:
Function calls: \$FUNC(\$\$\$)
Method calls: \$OBJ.\$METHOD(\$\$\$)
Imports: from \$MOD import \$\$\$
Decorators: @\$DECORATOR
If statements: if \$COND:
For loops: for \$VAR in \$ITER:
Try/except: try: \$\$\$ except \$EXC: \$\$\$
Context managers: with \$EXPR as \$VAR:
EOF
;;
js|javascript|ts|typescript)
cat <<EOF
Function declarations: function \$FUNC(\$\$\$) { \$\$\$ }
Arrow functions: (\$\$\$) => \$BODY
Class definitions: class \$CLASS { \$\$\$ }
Imports: import \$\$ from "\$MODULE"
Export default: export default \$EXPR
React components: function \$COMP() { \$\$\$ }
useState: const [\$STATE, \$SETTER] = useState(\$\$\$)
Async functions: async function \$FUNC(\$\$\$) { \$\$\$ }
EOF
;;
rs|rust)
cat <<EOF
Function definitions: fn \$FUNC(\$\$\$) { \$\$\$ }
Struct definitions: struct \$NAME { \$\$\$ }
Impl blocks: impl \$TYPE { \$\$\$ }
Trait impl: impl \$TRAIT for \$TYPE { \$\$\$ }
Macros: \$MACRO!(\$\$\$)
Match expressions: match \$EXPR { \$\$\$ }
Result returns: fn \$FUNC(\$\$\$) -> Result<\$\$\$> { \$\$\$ }
EOF
;;
go)
cat <<EOF
Function definitions: func \$FUNC(\$\$\$) \$\$\$ { \$\$\$ }
Method definitions: func (\$RECV \$TYPE) \$METHOD(\$\$\$) \$\$\$ { \$\$\$ }
Struct definitions: type \$NAME struct { \$\$\$ }
Interface definitions: type \$NAME interface { \$\$\$ }
Goroutines: go \$FUNC(\$\$\$)
Defer statements: defer \$FUNC(\$\$\$)
Error checks: if err != nil { \$\$\$ }
EOF
;;
zig)
cat <<EOF
Public functions: pub fn \$FUNC(\$\$\$) \$\$\$ { \$\$\$ }
Struct definitions: const \$NAME = struct { \$\$\$ };
Error handling: try \$EXPR
Test blocks: test "\$NAME" { \$\$\$ }
Comptime: comptime \$EXPR
EOF
;;
rb|ruby)
cat <<EOF
Method definitions: def \$METHOD(\$\$\$); \$\$\$; end
Class definitions: class \$CLASS; \$\$\$; end
Blocks with do/end: \$EXPR do |\$PARAM|; \$\$\$; end
Blocks with braces: \$EXPR { |\$PARAM| \$\$\$ }
Rails routes: get "\$PATH", to: "\$HANDLER"
ActiveRecord queries: \$MODEL.where(\$\$\$)
EOF
;;
*)
echo "Generic pattern: \$PATTERN"
;;
esac
}
# Show pattern examples
show_examples() {
print_header "Pattern Examples for $LANGUAGE"
get_pattern_examples "$LANGUAGE"
echo
}
# Interactive pattern input
get_pattern() {
show_examples
print_info "Enter your ast-grep pattern (or 'examples' to see more):"
echo -e "${YELLOW}Examples:${NC}"
echo " - def \$FUNC(\$\$\$):"
echo " - class \$CLASS:"
echo " - \$FUNC(\$\$\$)"
echo
read -rp "Pattern: " pattern
if [[ "$pattern" == "examples" ]]; then
show_examples
get_pattern
return
fi
if [[ -z "$pattern" ]]; then
print_error "Pattern cannot be empty"
exit 1
fi
echo "$pattern"
}
# Search with ast-grep and show results with fzf
search_pattern() {
local pattern="$1"
local lang="$2"
print_info "Searching for: $pattern"
echo
# Create temporary file for results
local results
results=$(mktemp)
# Run ast-grep and save results
if ! ast-grep --pattern "$pattern" -l "$lang" > "$results" 2>&1; then
print_error "ast-grep search failed"
cat "$results"
rm "$results"
exit 1
fi
# Check if we have results
if [[ ! -s "$results" ]]; then
print_warning "No matches found for pattern: $pattern"
rm "$results"
exit 0
fi
# Count results
local count
count=$(wc -l < "$results")
print_success "Found $count match(es)"
echo
# Interactive selection with fzf
local selected
selected=$(cat "$results" | fzf \
--preview "bat --color=always --highlight-line {2} {1}" \
--preview-window=right:60%:wrap \
--delimiter : \
--prompt "Select file > " \
--header "Press Enter to open, Ctrl-Y to copy path, Ctrl-C to exit" \
--bind "ctrl-y:execute-silent(echo {1} | pbcopy)+abort" \
--bind "ctrl-/:toggle-preview" \
--height=100% \
|| true)
rm "$results"
if [[ -z "$selected" ]]; then
print_info "No file selected"
exit 0
fi
# Extract file path and line number
local file line
file=$(echo "$selected" | cut -d: -f1)
line=$(echo "$selected" | cut -d: -f2)
# Show action menu
show_action_menu "$file" "$line" "$pattern" "$lang"
}
# Action menu for selected file
show_action_menu() {
local file="$1"
local line="$2"
local pattern="$3"
local lang="$4"
print_header "Selected: $file:$line"
echo "What would you like to do?"
echo
echo "1. Open in editor (\$EDITOR)"
echo "2. Show context (20 lines)"
echo "3. Show all matches in file"
echo "4. Copy file path to clipboard"
echo "5. Find usages of pattern in project"
echo "6. Exit"
echo
read -rp "Choice: " choice
case "$choice" in
1)
print_info "Opening $file at line $line..."
${EDITOR:-vim} "+$line" "$file"
;;
2)
print_header "Context around $file:$line"
bat --color=always --highlight-line "$line" --line-range "$((line-10)):$((line+10))" "$file"
;;
3)
print_header "All matches in $file"
ast-grep --pattern "$pattern" -l "$lang" "$file"
;;
4)
echo -n "$file" | pbcopy
print_success "Copied to clipboard: $file"
;;
5)
print_header "Finding usages in project..."
# Extract the identifier from the pattern
local identifier
identifier=$(echo "$pattern" | grep -oE '\$[A-Z_]+' | head -1 | sed 's/\$//')
if [[ -n "$identifier" ]]; then
print_info "Searching for references to: $identifier"
rg "\b$identifier\b" -t "$lang"
else
print_warning "Could not extract identifier from pattern"
fi
;;
6)
print_info "Exiting..."
exit 0
;;
*)
print_error "Invalid choice"
show_action_menu "$file" "$line" "$pattern" "$lang"
;;
esac
}
# Multi-pattern search mode
multi_pattern_search() {
print_header "Multi-Pattern Search"
print_info "Enter patterns (one per line, empty line to finish):"
local patterns=()
while true; do
read -rp "Pattern $(( ${#patterns[@]} + 1 )): " pattern
if [[ -z "$pattern" ]]; then
break
fi
patterns+=("$pattern")
done
if [[ ${#patterns[@]} -eq 0 ]]; then
print_error "No patterns provided"
exit 1
fi
print_info "Searching for ${#patterns[@]} pattern(s)..."
local all_results
all_results=$(mktemp)
for pattern in "${patterns[@]}"; do
ast-grep --pattern "$pattern" -l "$LANGUAGE" >> "$all_results" 2>&1 || true
done
# Remove duplicates and sort
sort -u "$all_results" | fzf \
--preview "bat --color=always {1}" \
--preview-window=right:60%:wrap \
--delimiter : \
--multi \
--prompt "Select files > " \
--header "Tab to select multiple, Enter to confirm" \
--bind "ctrl-a:select-all" \
--bind "ctrl-d:deselect-all" \
--height=100% \
|| true
rm "$all_results"
}
# Help message
show_help() {
cat <<EOF
${BLUE}Interactive Code Finder${NC}
Usage: $0 [OPTIONS]
Options:
-p, --pattern PATTERN Search for specific pattern
-l, --language LANG Set language (default: py)
-m, --multi Multi-pattern search mode
-e, --examples Show pattern examples and exit
-h, --help Show this help message
Languages:
py, python Python
js, javascript JavaScript
ts, typescript TypeScript
rs, rust Rust
go Go
zig Zig
rb, ruby Ruby
Examples:
$0 # Interactive mode
$0 -p "def \$FUNC(\$\$\$):" # Search for Python functions
$0 -l js -p "function \$F(\$\$\$)" # Search for JS functions
$0 -m # Multi-pattern search
$0 -e # Show pattern examples
Pattern Syntax:
\$VAR - Single node (identifier, expression)
\$\$\$ - Zero or more nodes (variadic)
\$\$ - Statement sequence
EOF
}
# Main function
main() {
local pattern=""
local multi_mode=false
# Check dependencies first
check_dependencies
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-p|--pattern)
pattern="$2"
shift 2
;;
-l|--language)
LANGUAGE="$2"
shift 2
;;
-m|--multi)
multi_mode=true
shift
;;
-e|--examples)
show_examples
exit 0
;;
-h|--help)
show_help
exit 0
;;
*)
print_error "Unknown option: $1"
show_help
exit 1
;;
esac
done
# Multi-pattern mode
if [[ "$multi_mode" == true ]]; then
multi_pattern_search
exit 0
fi
# Get pattern if not provided
if [[ -z "$pattern" ]]; then
pattern=$(get_pattern)
fi
# Search
search_pattern "$pattern" "$LANGUAGE"
}
main "$@"