Summary
Add built-in query and filtering capabilities to the Asana CLI using JMESPath, eliminating the need for external tools like jq for common filtering operations.
Motivation
Currently, users must use external tools to filter or transform CLI output:
# Current approach - requires jq installation
asana task list --format json | jq '.tasks[] | select(.completed == false)'
This creates several problems:
- External Dependency: Users must install and learn jq
- Broken Integration: Piping breaks the integrated CLI experience
- AI Agent Issues: External tools may not be available in coding agents (Claude Code, GitHub Copilot)
- Token Inefficiency: Full output generated before filtering
Proposed Solution
Implement built-in JMESPath query support using the industry-standard jmespath npm package (used by AWS CLI).
Why JMESPath?
After researching popular CLI tools (kubectl, aws-cli, gh, docker, terraform), JMESPath offers the best balance:
- ✅ Industry standard (AWS CLI, Azure CLI)
- ✅ Excellent TypeScript support
- ✅ Small bundle size (~50KB)
- ✅ Powerful filtering and transformations
- ✅ Good documentation and examples
- ✅ Moderate learning curve
Architecture Decision
See ADR-003: JMESPath Query Support for full analysis including alternatives considered (JSONPath, embedded JQ, simple filters).
Usage Examples
Basic Filtering
# Filter completed tasks
asana task list -q "tasks[?completed==\`true\`]"
# Filter by assignee
asana task list -q "tasks[?assignee.name==\`Alice\`]"
# Multiple conditions
asana task list -q "tasks[?completed==\`false\` && assignee.name==\`Alice\`]"
Field Projection
# Extract specific fields
asana task list -q "tasks[].{id: gid, name: name}"
# Get single field
asana task list -q "tasks[].name"
# Nested field access
asana task list -q "tasks[].assignee.name"
Advanced Queries
# Get first task
asana task list -q "tasks[0]"
# Count tasks
asana task list -q "length(tasks)"
# Combine filter and projection
asana task list -q "tasks[?completed==\`false\`].{id: gid, name: name}"
With Output Formats
# Query with TOON output (default)
asana task list -q "tasks[?completed==\`false\`]"
# Query with JSON output
asana task list -q "tasks[].name" -f json
# Query with Plain output
asana task list -q "tasks[?completed==\`false\`]" -f plain
Technical Specifications
Implementation Architecture
// src/utils/formatter.ts
import jmespath from 'jmespath'
export interface FormatterOptions {
format: OutputFormat
colors?: boolean
query?: string // JMESPath expression
}
export function formatOutput(data: any, options: FormatterOptions): string {
const { format, colors = false, query } = options
// Apply JMESPath query if provided
let result = data
if (query) {
try {
result = jmespath.search(data, query)
} catch (error: any) {
throw new QueryError(error, query)
}
}
// Format output (existing logic)
return formatByType(result, format, colors)
}
CLI Integration
// src/index.ts
program
.option('-f, --format <type>', 'Output format', 'toon')
.option('-q, --query <expression>', 'JMESPath query to filter/transform output')
.addHelpText('after', `
Query Examples:
Filter tasks: -q "tasks[?completed==\`false\`]"
Extract fields: -q "tasks[].{id: gid, name: name}"
Get first item: -q "tasks[0]"
Learn more: https://jmespath.org/tutorial.html
`)
Processing Pipeline
Data Fetching → JMESPath Query → Format Selection → Output
↓ ↓ ↓ ↓
API Call Filter/Transform TOON/JSON/Plain stdout
Error Handling
Provide helpful error messages with examples:
Query Error: Unexpected token at position 15
Query: tasks[?completed==true] (missing backticks around boolean)
Examples:
Filter: [?completed==`true`]
Project: [].{id: gid, name: name}
First: [0]
Learn more: https://jmespath.org/tutorial.html
Implementation Checklist
Phase 1: Core Integration
Phase 2: Command Integration
Phase 3: Testing
Phase 4: Documentation
Phase 5: Polish
Comparison with Alternatives
| Approach |
Power |
Bundle Size |
Learning Curve |
Industry Adoption |
TypeScript Support |
| JMESPath (Selected) |
⭐⭐⭐⭐ |
Small (~50KB) |
Moderate |
High (AWS, Azure) |
Excellent |
| JSONPath |
⭐⭐⭐ |
Medium (~100KB) |
Moderate |
Medium (kubectl) |
Good |
| Embedded JQ |
⭐⭐⭐⭐⭐ |
Large (~500KB) |
Hard |
High (DevOps) |
Fair |
| Simple Filters |
⭐⭐ |
None |
Easy |
Low (docker) |
Excellent |
| External Tools |
⭐⭐⭐⭐⭐ |
None |
Varies |
High |
N/A |
Dependencies
{
"dependencies": {
"jmespath": "^0.16.0"
},
"devDependencies": {
"@types/jmespath": "^0.15.2"
}
}
Package Details:
- Weekly Downloads: 1M+
- Bundle Size: ~50KB minified
- License: Apache-2.0
- Maintenance: Active (AWS-backed)
Benefits
- Zero External Dependencies: No need to install jq, jless, or other tools
- Integrated Experience: Single command for fetch + filter + format
- AI Agent Compatible: Works in any environment
- Token Efficient: Filter before formatting reduces output tokens
- Industry Standard: Follows AWS CLI pattern
- Well Documented: JMESPath has excellent tutorials
- TypeScript Native: Full type support
- Backward Compatible: Query is optional
Breaking Changes
None - this is an additive feature. Existing usage continues to work unchanged.
Related
References
Summary
Add built-in query and filtering capabilities to the Asana CLI using JMESPath, eliminating the need for external tools like
jqfor common filtering operations.Motivation
Currently, users must use external tools to filter or transform CLI output:
This creates several problems:
Proposed Solution
Implement built-in JMESPath query support using the industry-standard
jmespathnpm package (used by AWS CLI).Why JMESPath?
After researching popular CLI tools (kubectl, aws-cli, gh, docker, terraform), JMESPath offers the best balance:
Architecture Decision
See ADR-003: JMESPath Query Support for full analysis including alternatives considered (JSONPath, embedded JQ, simple filters).
Usage Examples
Basic Filtering
Field Projection
Advanced Queries
With Output Formats
Technical Specifications
Implementation Architecture
CLI Integration
Processing Pipeline
Error Handling
Provide helpful error messages with examples:
Implementation Checklist
Phase 1: Core Integration
jmespathdependency to package.json@types/jmespathdev dependencyFormatterOptionsinterface withqueryfieldformatOutput()function--query/-qCLI flag to main programQueryErrorclass with helpful error messagesPhase 2: Command Integration
task listcommand to pass query optiontask getcommand to pass query optionauth whoamicommand to pass query optiontask createcommand to pass query optionPhase 3: Testing
Phase 4: Documentation
Phase 5: Polish
Comparison with Alternatives
Dependencies
{ "dependencies": { "jmespath": "^0.16.0" }, "devDependencies": { "@types/jmespath": "^0.15.2" } }Package Details:
Benefits
Breaking Changes
None - this is an additive feature. Existing usage continues to work unchanged.
Related
References