Support catchall nodes
diff --git a/pkg/matcher/matcher.go b/pkg/matcher/matcher.go
index 8780550..a26404b 100644
--- a/pkg/matcher/matcher.go
+++ b/pkg/matcher/matcher.go
@@ -25,22 +25,37 @@
 
 // Replace replaces all occurrences of the pattern in the given node with the replacement
 func (m *Matcher) Replace(node ast.Node) ast.Node {
+	// If this node matches the pattern, create replacement while preserving outer structure
 	if m.Match(node) {
-		return m.cloneNode(m.replacement.Root)
+		switch node.(type) {
+		case *ast.Token:
+			// For Token nodes, preserve the Token wrapper but replace its wrap
+			newToken := &ast.Token{
+				Wrap: m.cloneNode(m.replacement.Root),
+			}
+			return newToken
+		default:
+			return m.cloneNode(m.replacement.Root)
+		}
 	}
 
+	// Otherwise recursively process children
 	switch n := node.(type) {
 	case *ast.Token:
-		n.Wrap = m.Replace(n.Wrap)
-		return n
+		newToken := &ast.Token{
+			Wrap: m.Replace(n.Wrap),
+		}
+		return newToken
 
 	case *ast.TermGroup:
 		newOperands := make([]ast.Node, len(n.Operands))
 		for i, op := range n.Operands {
 			newOperands[i] = m.Replace(op)
 		}
-		n.Operands = newOperands
-		return n
+		return &ast.TermGroup{
+			Operands: newOperands,
+			Relation: n.Relation,
+		}
 
 	case *ast.CatchallNode:
 		newNode := &ast.CatchallNode{
@@ -72,52 +87,80 @@
 		return false
 	}
 
-	switch p := pattern.(type) {
-	case *ast.Token:
-		if t, ok := node.(*ast.Token); ok {
-			return m.matchNode(t.Wrap, p.Wrap)
+	// Handle pattern being a Token
+	if pToken, ok := pattern.(*ast.Token); ok {
+		if nToken, ok := node.(*ast.Token); ok {
+			return m.matchNode(nToken.Wrap, pToken.Wrap)
 		}
 		return false
+	}
 
-	case *ast.TermGroup:
-		// If we're matching against a term, try to match it against any operand
-		if t, ok := node.(*ast.Term); ok && p.Relation == ast.OrRelation {
-			for _, op := range p.Operands {
-				if m.matchNode(t, op) {
+	// Handle pattern being a Term
+	if pTerm, ok := pattern.(*ast.Term); ok {
+		// Direct term to term matching
+		if t, ok := node.(*ast.Term); ok {
+			return t.Foundry == pTerm.Foundry &&
+				t.Key == pTerm.Key &&
+				t.Layer == pTerm.Layer &&
+				t.Match == pTerm.Match &&
+				(pTerm.Value == "" || t.Value == pTerm.Value)
+		}
+		// If node is a Token, check its wrap
+		if tkn, ok := node.(*ast.Token); ok {
+			if tkn.Wrap == nil {
+				return false
+			}
+			return m.matchNode(tkn.Wrap, pattern)
+		}
+		// If node is a TermGroup, check its operands
+		if tg, ok := node.(*ast.TermGroup); ok {
+			for _, op := range tg.Operands {
+				if m.matchNode(op, pattern) {
+					return true
+				}
+			}
+			return false
+		}
+		// If node is a CatchallNode, check its wrap and operands
+		if c, ok := node.(*ast.CatchallNode); ok {
+			if c.Wrap != nil && m.matchNode(c.Wrap, pattern) {
+				return true
+			}
+			for _, op := range c.Operands {
+				if m.matchNode(op, pattern) {
+					return true
+				}
+			}
+			return false
+		}
+		return false
+	}
+
+	// Handle pattern being a TermGroup
+	if pGroup, ok := pattern.(*ast.TermGroup); ok {
+		// For OR relations, check if any operand matches the node
+		if pGroup.Relation == ast.OrRelation {
+			for _, pOp := range pGroup.Operands {
+				if m.matchNode(node, pOp) {
 					return true
 				}
 			}
 			return false
 		}
 
-		// If we're matching against a term group
-		if t, ok := node.(*ast.TermGroup); ok {
-			if t.Relation != p.Relation {
+		// For AND relations, node must be a TermGroup with matching relation
+		if tg, ok := node.(*ast.TermGroup); ok {
+			if tg.Relation != pGroup.Relation {
 				return false
 			}
-
-			if p.Relation == ast.OrRelation {
-				// For OR relation, at least one operand must match
-				for _, pOp := range p.Operands {
-					for _, tOp := range t.Operands {
-						if m.matchNode(tOp, pOp) {
-							return true
-						}
-					}
-				}
+			// Check that all pattern operands match in any order
+			if len(tg.Operands) < len(pGroup.Operands) {
 				return false
 			}
-
-			// For AND relation, all pattern operands must match
-			if len(t.Operands) < len(p.Operands) {
-				return false
-			}
-
-			// Try to match pattern operands against node operands in any order
-			matched := make([]bool, len(t.Operands))
-			for _, pOp := range p.Operands {
+			matched := make([]bool, len(tg.Operands))
+			for _, pOp := range pGroup.Operands {
 				found := false
-				for j, tOp := range t.Operands {
+				for j, tOp := range tg.Operands {
 					if !matched[j] && m.matchNode(tOp, pOp) {
 						matched[j] = true
 						found = true
@@ -130,65 +173,28 @@
 			}
 			return true
 		}
-		return false
 
-	case *ast.CatchallNode:
-		// For catchall nodes, we need to check both wrap and operands
-		if t, ok := node.(*ast.CatchallNode); ok {
-			// If pattern has wrap, match it
-			if p.Wrap != nil && !m.matchNode(t.Wrap, p.Wrap) {
+		// If node is a Token, check its wrap
+		if tkn, ok := node.(*ast.Token); ok {
+			if tkn.Wrap == nil {
 				return false
 			}
+			return m.matchNode(tkn.Wrap, pattern)
+		}
 
-			// If pattern has operands, match them
-			if len(p.Operands) > 0 {
-				if len(t.Operands) < len(p.Operands) {
-					return false
-				}
-
-				// Try to match pattern operands against node operands in any order
-				matched := make([]bool, len(t.Operands))
-				for _, pOp := range p.Operands {
-					found := false
-					for j, tOp := range t.Operands {
-						if !matched[j] && m.matchNode(tOp, pOp) {
-							matched[j] = true
-							found = true
-							break
-						}
-					}
-					if !found {
-						return false
-					}
-				}
+		// If node is a CatchallNode, check its wrap and operands
+		if c, ok := node.(*ast.CatchallNode); ok {
+			if c.Wrap != nil && m.matchNode(c.Wrap, pattern) {
 				return true
 			}
-
-			// If no wrap or operands to match, it's a match
-			return true
-		}
-		return false
-
-	case *ast.Term:
-		// If we're matching against a term group with OR relation,
-		// try to match against any of its operands
-		if t, ok := node.(*ast.TermGroup); ok && t.Relation == ast.OrRelation {
-			for _, op := range t.Operands {
-				if m.matchNode(op, p) {
+			for _, op := range c.Operands {
+				if m.matchNode(op, pattern) {
 					return true
 				}
 			}
 			return false
 		}
 
-		// Direct term to term matching
-		if t, ok := node.(*ast.Term); ok {
-			return t.Foundry == p.Foundry &&
-				t.Key == p.Key &&
-				t.Layer == p.Layer &&
-				t.Match == p.Match &&
-				(p.Value == "" || t.Value == p.Value)
-		}
 		return false
 	}