Tree-Sitter-Bash Parser API: How to Use the MoonshotAI Parser Safely

TLDR: The @moonshot-ai/tree-sitter-bash package provides a pure-TypeScript Bash parser with a parse(source, options?) function that returns a discriminated ParseResult. Use it safely by setting budget limits, checking result.ok, and handling error states.

The tree-sitter-bash parser API from MoonshotAI offers a deterministic, type-safe way to parse Bash scripts in TypeScript without native dependencies. This complete guide covers the core API, safety guarantees, and best practices for reliable integration.

Understanding the Parse Function

The parse() function in src/parse.ts serves as the primary entry point for all tree-sitter-bash parser operations.

parse(source: string, options?: ParseOptions): ParseResult

The function accepts a Bash source string and optional configuration. It returns a discriminated union: { ok: true, root: SyntaxNode } on success, or { ok: false, error: ParseError } when parsing fails.

Key parameters to understand:

  • source — The complete Bash script to parse
  • ParseOptions — Extends ParseBudget from src/budget.ts with timeoutMs and maxNodes

ParseBudget: Enforcing Safe Execution

Safety in the tree-sitter-bash parser API centers on the budget system defined in src/budget.ts.

The parser enforces deterministic limits to prevent runaway execution:

Budget Parameter Default Value Purpose
timeoutMs 50 ms Maximum parsing duration
maxNodes 50,000 Maximum AST nodes generated

When limits are exceeded, the parser throws a custom Aborted error. This prevents denial-of-service scenarios with malformed or malicious input.

import { parse } from '@moonshot-ai/tree-sitter-bash';

// Safe parsing with strict limits for untrusted input
const result = parse(untrustedScript, {
  timeoutMs: 30,
  maxNodes: 5_000
});

if (!result.ok) {
  // Handle budget exhaustion or parse error
  console.error('Parsing failed:', result.error);
}

Working with SyntaxNode Objects

The SyntaxNode interface in src/node.ts represents elements of the parsed Bash AST.

Each node provides:

  • type — Grammar node type (e.g., "program", "command", "variable_name")
  • text — Source text span
  • startByte / endByte — Position in source
  • hasError — Boolean indicating parse errors in this subtree
  • descendants — Array of child nodes for tree traversal

Helper functions assist common operations:

import { parse, descendantsOfType, createNode } from '@moonshot-ai/tree-sitter-bash';

const result = parse('echo $HOME && ls -la');

if (result.ok) {
  // Find all variable references
  const variables = descendantsOfType(result.root, 'variable_name');
  
  // Check for partial parse errors
  const errorNodes = result.root.descendants.filter(n => n.hasError);
}

Safe Usage Patterns for Tree-Sitter-Bash

Follow these four rules to use the tree-sitter-bash parser API safely in production.

1. Always Validate Parse Results

Never access result.root without confirming success.

const result = parse(script);

// Correct: check ok before accessing root
if (result.ok) {
  analyzeTree(result.root);
} else {
  failGracefully(result.error);
}

// Dangerous: throws if parsing failed
doSomething(result.root); // Never do this

2. Respect Node Error State

A successful parse may still contain error nodes indicating incomplete or invalid syntax.

if (result.ok) {
  const hasErrors = result.root.descendants.some(n => n.hasError);
  
  if (hasErrors) {
    // Treat as unreliable — partial parse only
    return { confidence: 'low', tree: result.root };
  }
}

3. Configure Appropriate Budgets

Match limits to your threat model and latency requirements.

// Interactive editing: tight limits for responsiveness
const quickParse = parse(script, { timeoutMs: 10, maxNodes: 1_000 });

// Batch analysis of known-good files: relaxed limits
const thoroughParse = parse(script, { timeoutMs: 500, maxNodes: 500_000 });

4. Maintain Parser Purity

The tree-sitter-bash parser is pure: identical inputs produce identical outputs. Avoid mutable state patterns that could introduce non-determinism.

// Safe: parser has no side effects
const r1 = parse(source);
const r2 = parse(source);
// r1 and r2 are structurally equal

Complete Integration Example

This production-ready pattern demonstrates safe tree-sitter-bash parser API usage:

import { parse, SyntaxNode, ParseError } from '@moonshot-ai/tree-sitter-bash';

interface ParseAttempt {
  success: boolean;
  tree?: SyntaxNode;
  error?: ParseError;
  hasSyntaxErrors: boolean;
  confidence: 'high' | 'medium' | 'low' | 'failed';
}

function safeParseBash(
  source: string,
  budget = { timeoutMs: 50, maxNodes: 50_000 }
): ParseAttempt {
  const result = parse(source, budget);
  
  if (!result.ok) {
    return {
      success: false,
      error: result.error,
      hasSyntaxErrors: true,
      confidence: 'failed'
    };
  }
  
  const errorNodes = result.root.descendants.filter(n => n.hasError);
  const hasSyntaxErrors = errorNodes.length > 0;
  
  return {
    success: true,
    tree: result.root,
    hasSyntaxErrors,
    confidence: hasSyntaxErrors ? 'low' : 'high'
  };
}

// Usage
const analysis = safeParseBash(userInput, { timeoutMs: 30, maxNodes: 10_000 });

if (!analysis.success || analysis.confidence === 'low') {
  // Degrade: skip advanced features, show warning
  return basicFallbackMode(userInput);
}

// Proceed with full AST analysis
performRefactoring(analysis.tree!);

Key Source Files Reference

File Location Responsibility
src/index.ts Package root Public API exports (parse, SyntaxNode, ParseBudget)
src/parse.ts Core implementation parse() function and ParseResult types
src/node.ts AST structures SyntaxNode interface and traversal helpers
src/budget.ts Safety enforcement ParseBudget, Aborted error class
src/lexer.ts Tokenization Low-level lexical scanner
src/grammar.ts Language definition Reserved words, operators, grammar constants

Summary

  • Entry point: parse() in src/parse.ts returns a discriminated union — always check result.ok
  • Safety mechanism: ParseBudget in src/budget.ts enforces timeoutMs and maxNodes limits with Aborted errors
  • Error handling: Treat !result.ok and node.hasError as unreliable states requiring graceful degradation
  • Pure design: Parser has no side effects; reuse for deterministic results

Frequently Asked Questions

What happens if parsing exceeds the budget limits?

The parser throws an Aborted error from src/budget.ts, and parse() returns { ok: false, error }. This prevents resource exhaustion from malicious or pathological input. Set limits appropriate to your workload and always handle the error case.

Can I parse partial or syntactically invalid Bash scripts?

Yes, but with caution. The parser may return result.ok: true with hasError: true on specific nodes. These error nodes indicate where the grammar failed. Inspect node.hasError throughout the tree before trusting any analysis results.

How do I find specific node types in the AST?

Use descendantsOfType(root, typeString) from src/node.ts or filter root.descendants manually. Both approaches traverse the complete tree — consider caching results for large documents to avoid repeated walks.

Is this parser API compatible with standard tree-sitter tools?

The @moonshot-ai/tree-sitter-bash API mirrors the official tree-sitter grammar but implements a pure-TypeScript parser. While node types and tree structure align with tree-sitter conventions, this is a standalone implementation without WebAssembly dependencies.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →