Tree HuggerTree Hugger

2025Solo DeveloperActive

A friendly layer on top of tree-sitter, with an MCP server for AI agents.

Tree-sitter is incredibly powerful for code analysis, but its API is hard to use—both for humans and for AI agents. The node type names are verbose, pattern matching requires manual tree traversal, and transformations involve byte offset math. Tree Hugger wraps tree-sitter with an interface that feels more like writing CSS selectors than navigating an AST.

The library lets you query code with patterns like ‘function[async]’ or ‘class method:has(call[text*="fetch"])’ instead of enumerating every tree-sitter node type. It ships with smart transformations—renaming an identifier skips occurrences inside strings and comments, removing a function call removes the whole statement, and insertions match the surrounding indentation.

The MCP server exposes all of this to AI agents as 12 tools covering analysis, transformation, and navigation. An agent can parse a file, find all async functions with debug logging, rename an identifier safely across the codebase, and remove unused imports—all through MCP tool calls.

Find all async functions
tree-sitter
import Parser from 'tree-sitter';
import JavaScript from 'tree-sitter-javascript';

const parser = new Parser();
parser.setLanguage(JavaScript);
const tree = parser.parse(code);

const asyncFunctions = [];
function walk(node) {
  if (
    node.type === 'function_declaration' ||
    node.type === 'function_expression' ||
    node.type === 'arrow_function' ||
    node.type === 'method_definition'
  ) {
    if (node.childForFieldName('async')
        || node.text.startsWith('async')) {
      asyncFunctions.push(node);
    }
  }
  for (const child of node.children) walk(child);
}
walk(tree.rootNode);
Tree Hugger
import { parse } from 'tree-hugger-js';

const fns = parse(code).findAll('function[async]');
Parse and Query
import { parse } from 'tree-hugger-js';

const tree = parse('app.tsx');

// CSS-like selectors instead of raw tree-sitter node types
const asyncFns = tree.findAll('function[async]');
const fetchCalls = tree.findAll('call[text*="fetch"]');
const styledJsx = tree.findAll('jsx:has(jsx-attribute[name="className"])');

// Built-in queries
const hooks = tree.hooks();       // React hooks (useX pattern)
const imports = tree.imports();   // All import statements
Transform Code
const result = tree.transform()
  .rename('getUserData', 'fetchUserProfile')
  .remove('console.log')
  .removeUnusedImports()
  .toString();
Stack
TypeScriptNode.jstree-sitterMCP
Features
  • CSS-like pattern selectors for AST queries
  • Context-aware transformations (rename, remove, insert)
  • Auto-detection of JS, TS, JSX, and TSX
  • Scope analysis and variable binding lookup
  • 12 MCP tools for analysis, transforms, and navigation
  • Smart unused import removal