# File lib/automateit/interpreter.rb, line 402
    def invoke(recipe)
      filenames = [recipe]
      filenames << File.join(project, "recipes", recipe) if project
      filenames << File.join(project, "recipes", recipe + ".rb") if project

      for filename in filenames
        log.debug(PNOTE+" invoking "+filename)
        if File.exists?(filename)
          data = File.read(filename)
          begin
            return instance_eval(data, filename, 1)
          rescue Exception => e
            if @friendly_exceptions
              # TODO Extract this routine and its companion in HelpfulERB

              # Capture initial stack in case we add a debug/breakpoint after this
              stack = caller

              # Extract trace for recipe after the Interpreter#invoke call
              preresult = []
              for line in e.backtrace
                # Stop at the Interpreter#invoke call
                break if line == stack.first
                preresult << line
              end

              # Extract the recipe filename
              preresult.last.match(/^([^:]+):(\d+):in `invoke'/)
              recipe = $1

              # Extract trace for most recent block
              result = []
              for line in preresult
                # Ignore manager wrapper and dispatch methods
                next if line =~ %r{lib/automateit/.+manager\.rb:\d+:in `.+'$}
                result << line
                # Stop at the first mention of this recipe
                break if line =~ /^#{recipe}/
              end

              # Extract line number
              if e.is_a?(SyntaxError)
                line_number = e.message.match(/^[^:]+:(\d+):/)[1].to_i
              else
                result.last.match(/^([^:]+):(\d+):in `invoke'/)
                line_number = $2.to_i
              end

              msg = "Problem with recipe '#{recipe}' at line #{line_number}\n"

              # Extract recipe text
              begin
                lines = File.read(recipe).split(/\n/)

                min = line_number - 7
                min = 0 if min < 0

                max = line_number + 1
                max = lines.size if max > lines.size

                width = max.to_s.size

                for i in min..max
                  n = i+1
                  marker = n == line_number ? "*" : ""
                  msg << "\n%2s %#{width}i %s" % [marker, n, lines[i]]
                end

                msg << "\n"
              rescue Exception => e
                # Ignore
              end

              msg << "\n(#{e.exception.class}) #{e.message}"

              # Append shortened trace
              for line in result
                msg << "\n  "+line
              end

              # Remove project path
              msg.gsub!(/#{@project}\/?/, '') if @project

              raise AutomateIt::Error.new(msg, e)
            else
              raise e
            end
          end
        end
      end
      raise Errno::ENOENT.new(recipe)
    end