Skip to content

Command line usage tutorial

This tutorial walks you trough running SWE-agent from the command line. Beginners might also be interested in the our web-based GUI (see here). This tutorial focuses on using SWE-agent as a tool to solve individual issues. Benchmarking SWE-agent is covered separately.

Getting started

For the CLI, use the run.py script. Let's start with an absolutely trivial example and solve an issue about a simple syntax error (swe-agent/test-repo #1)

python run.py \
  --model_name gpt4 \
  --data_path https://github.com/SWE-agent/test-repo/issues/1 \
  --config_file config/default_from_url.yaml \
  --per_instance_cost_limit 2.00
Output
INFO     📙 Arguments: actions:
           apply_patch_locally: false
           open_pr: false
           push_gh_repo_url: ''
           skip_if_commits_reference_issue: true
         agent:
           config:
             _commands:
             - arguments:
                 line_number:
                   description: the line number to move the window to (if not provided, the
                     window will start at the top of the file)
                   required: false
                   type: integer
                 path:
                   description: the path to the file to open
                   required: true
                   type: string
               code: 'open() {    if [ -z "$1" ]    then        echo "Usage: open <file>"        return    fi    #
                 Check if the second argument is provided    if [ -n "$2" ]; then        #
                 Check if the provided argument is a valid number        if ! [[ $2 =~ ^[0-9]+$
                 ]]; then            echo "Usage: open <file> [<line_number>]"            echo
                 "Error: <line_number> must be a number"            return  # Exit if the line
                 number is not valid        fi        local max_line=$(awk ''END {print NR}''
                 $1)        if [ $2 -gt $max_line ]; then            echo "Warning: <line_number>
                 ($2) is greater than the number of lines in the file ($max_line)"            echo
                 "Warning: Setting <line_number> to $max_line"            local line_number=$(jq
                 -n "$max_line")  # Set line number to max if greater than max        elif
                 [ $2 -lt 1 ]; then            echo "Warning: <line_number> ($2) is less than
                 1"            echo "Warning: Setting <line_number> to 1"            local
                 line_number=$(jq -n "1")  # Set line number to 1 if less than 1        else            local
                 OFFSET=$(jq -n "$WINDOW/6" | jq ''floor'')            local line_number=$(jq
                 -n "[$2 + $WINDOW/2 - $OFFSET, 1] | max | floor")        fi    else        local
                 line_number=$(jq -n "$WINDOW/2")  # Set default line number if not provided    fi    if
                 [ -f "$1" ]; then        export CURRENT_FILE=$(realpath $1)        export
                 CURRENT_LINE=$line_number        _constrain_line        _print    elif [ -d
                 "$1" ]; then        echo "Error: $1 is a directory. You can only open files.
                 Use cd or ls to navigate directories."    else        echo "File $1 not found"    fi}'
               docstring: opens the file at the given path in the editor. If line_number is
                 provided, the window will be move to include that line
               end_name: null
               name: open
               signature: open <path> [<line_number>]
             - arguments:
                 line_number:
                   description: the line number to move the window to
                   required: true
                   type: integer
               code: 'goto() {    if [ $# -gt 1 ]; then        echo "goto allows only one line
                 number at a time."        return    fi    if [ -z "$CURRENT_FILE" ]    then        echo
                 "No file open. Use the open command first."        return    fi    if [ -z
                 "$1" ]    then        echo "Usage: goto <line>"        return    fi    if
                 ! [[ $1 =~ ^[0-9]+$ ]]    then        echo "Usage: goto <line>"        echo
                 "Error: <line> must be a number"        return    fi    local max_line=$(awk
                 ''END {print NR}'' $CURRENT_FILE)    if [ $1 -gt $max_line ]    then        echo
                 "Error: <line> must be less than or equal to $max_line"        return    fi    local
                 OFFSET=$(jq -n "$WINDOW/6" | jq ''floor'')    export CURRENT_LINE=$(jq -n
                 "[$1 + $WINDOW/2 - $OFFSET, 1] | max | floor")    _constrain_line    _print}'
               docstring: moves the window to show <line_number>
               end_name: null
               name: goto
               signature: goto <line_number>
             - arguments: null
               code: scroll_down() {    if [ -z "$CURRENT_FILE" ]    then        echo "No file
                 open. Use the open command first."        return    fi    export CURRENT_LINE=$(jq
                 -n "$CURRENT_LINE + $WINDOW - $OVERLAP")    _constrain_line    _print}
               docstring: moves the window down {WINDOW} lines
               end_name: null
               name: scroll_down
               signature: scroll_down
             - arguments: null
               code: scroll_up() {    if [ -z "$CURRENT_FILE" ]    then        echo "No file
                 open. Use the open command first."        return    fi    export CURRENT_LINE=$(jq
                 -n "$CURRENT_LINE - $WINDOW + $OVERLAP")    _constrain_line    _print}
               docstring: moves the window down {WINDOW} lines
               end_name: null
               name: scroll_up
               signature: scroll_up
             - arguments:
                 filename:
                   description: the name of the file to create
                   required: true
                   type: string
               code: "create() {    if [ -z \"$1\" ]; then        echo \"Usage: create <filename>\"\
                 \        return    fi    # Check if the file already exists    if [ -e \"\
                 $1\" ]; then        echo \"Error: File '$1' already exists.\"\t\topen \"$1\"\
                 \        return    fi    # Create the file an empty new line    printf \"\\\
                 n\" > \"$1\"    # Use the existing open command to open the created file \
                 \   open \"$1\"}"
               docstring: creates and opens a new file with the given name
               end_name: null
               name: create
               signature: create <filename>
             - arguments: null
               code: 'submit() {    cd $ROOT    # Check if the patch file exists and is non-empty    if
                 [ -s "/root/test.patch" ]; then        # Apply the patch in reverse        git
                 apply -R < "/root/test.patch"    fi    git add -A    git diff --cached > model.patch    echo
                 "<<SUBMISSION||"    cat model.patch    echo "||SUBMISSION>>"}'
               docstring: submits your current code and terminates the session
               end_name: null
               name: submit
               signature: submit
             - arguments:
                 dir:
                   description: the directory to search in (if not provided, searches in the
                     current directory)
                   required: false
                   type: string
                 search_term:
                   description: the term to search for
                   required: true
                   type: string
               code: 'search_dir() {    if [ $# -eq 1 ]; then        local search_term="$1"        local
                 dir="./"    elif [ $# -eq 2 ]; then        local search_term="$1"        if
                 [ -d "$2" ]; then            local dir="$2"        else            echo "Directory
                 $2 not found"            return        fi    else        echo "Usage: search_dir
                 <search_term> [<dir>]"        return    fi    dir=$(realpath "$dir")    local
                 matches=$(find "$dir" -type f ! -path ''*/.*'' -exec grep -nIH -- "$search_term"
                 {} + | cut -d: -f1 | sort | uniq -c)    # if no matches, return    if [ -z
                 "$matches" ]; then        echo "No matches found for \"$search_term\" in $dir"        return    fi    #
                 Calculate total number of matches    local num_matches=$(echo "$matches" |
                 awk ''{sum+=$1} END {print sum}'')    # calculate total number of files matched    local
                 num_files=$(echo "$matches" | wc -l | awk ''{$1=$1; print $0}'')    # if num_files
                 is > 100, print an error    if [ $num_files -gt 100 ]; then        echo "More
                 than $num_files files matched for \"$search_term\" in $dir. Please narrow
                 your search."        return    fi    echo "Found $num_matches matches for
                 \"$search_term\" in $dir:"    echo "$matches" | awk ''{$2=$2; gsub(/^\.+\/+/,
                 "./", $2); print $2 " ("$1" matches)"}''    echo "End of matches for \"$search_term\"
                 in $dir"}'
               docstring: searches for search_term in all files in dir. If dir is not provided,
                 searches in the current directory
               end_name: null
               name: search_dir
               signature: search_dir <search_term> [<dir>]
             - arguments:
                 file:
                   description: the file to search in (if not provided, searches in the current
                     open file)
                   required: false
                   type: string
                 search_term:
                   description: the term to search for
                   required: true
                   type: string
               code: 'search_file() {    # Check if the first argument is provided    if [
                 -z "$1" ]; then        echo "Usage: search_file <search_term> [<file>]"        return    fi    #
                 Check if the second argument is provided    if [ -n "$2" ]; then        #
                 Check if the provided argument is a valid file        if [ -f "$2" ]; then            local
                 file="$2"  # Set file if valid        else            echo "Usage: search_file
                 <search_term> [<file>]"            echo "Error: File name $2 not found. Please
                 provide a valid file name."            return  # Exit if the file is not valid        fi    else        #
                 Check if a file is open        if [ -z "$CURRENT_FILE" ]; then            echo
                 "No file open. Use the open command first."            return  # Exit if no
                 file is open        fi        local file="$CURRENT_FILE"  # Set file to the
                 current open file    fi    local search_term="$1"    file=$(realpath "$file")    #
                 Use grep to directly get the desired formatted output    local matches=$(grep
                 -nH -- "$search_term" "$file")    # Check if no matches were found    if [
                 -z "$matches" ]; then        echo "No matches found for \"$search_term\" in
                 $file"        return    fi    # Calculate total number of matches    local
                 num_matches=$(echo "$matches" | wc -l | awk ''{$1=$1; print $0}'')    # calculate
                 total number of lines matched    local num_lines=$(echo "$matches" | cut -d:
                 -f1 | sort | uniq | wc -l | awk ''{$1=$1; print $0}'')    # if num_lines is
                 > 100, print an error    if [ $num_lines -gt 100 ]; then        echo "More
                 than $num_lines lines matched for \"$search_term\" in $file. Please narrow
                 your search."        return    fi    # Print the total number of matches and
                 the matches themselves    echo "Found $num_matches matches for \"$search_term\"
                 in $file:"    echo "$matches" | cut -d: -f1-2 | sort -u -t: -k2,2n | while
                 IFS=: read -r filename line_number; do        echo "Line $line_number:$(sed
                 -n "${line_number}p" "$file")"    done    echo "End of matches for \"$search_term\"
                 in $file"}'
               docstring: searches for search_term in file. If file is not provided, searches
                 in the current open file
               end_name: null
               name: search_file
               signature: search_file <search_term> [<file>]
             - arguments:
                 dir:
                   description: the directory to search in (if not provided, searches in the
                     current directory)
                   required: false
                   type: string
                 file_name:
                   description: the name of the file to search for
                   required: true
                   type: string
               code: 'find_file() {    if [ $# -eq 1 ]; then        local file_name="$1"        local
                 dir="./"    elif [ $# -eq 2 ]; then        local file_name="$1"        if
                 [ -d "$2" ]; then            local dir="$2"        else            echo "Directory
                 $2 not found"            return        fi    else        echo "Usage: find_file
                 <file_name> [<dir>]"        return    fi    dir=$(realpath "$dir")    local
                 matches=$(find "$dir" -type f -name "$file_name")    # if no matches, return    if
                 [ -z "$matches" ]; then        echo "No matches found for \"$file_name\" in
                 $dir"        return    fi    # Calculate total number of matches    local
                 num_matches=$(echo "$matches" | wc -l | awk ''{$1=$1; print $0}'')    echo
                 "Found $num_matches matches for \"$file_name\" in $dir:"    echo "$matches"
                 | awk ''{print $0}''}'
               docstring: finds all files with the given name in dir. If dir is not provided,
                 searches in the current directory
               end_name: null
               name: find_file
               signature: find_file <file_name> [<dir>]
             - arguments:
                 end_line:
                   description: the line number to end the edit at (inclusive)
                   required: true
                   type: integer
                 replacement_text:
                   description: the text to replace the current selection with
                   required: true
                   type: string
                 start_line:
                   description: the line number to start the edit at
                   required: true
                   type: integer
               code: 'edit() {    if [ -z "$CURRENT_FILE" ]    then        echo ''No file open.
                 Use the `open` command first.''        return    fi    local start_line="$(echo
                 $1: | cut -d: -f1)"    local end_line="$(echo $1: | cut -d: -f2)"    if [
                 -z "$start_line" ] || [ -z "$end_line" ]    then        echo "Usage: edit
                 <start_line>:<end_line>"        return    fi    local re=''^[0-9]+$''    if
                 ! [[ $start_line =~ $re ]]; then        echo "Usage: edit <start_line>:<end_line>"        echo
                 "Error: start_line must be a number"        return    fi    if ! [[ $end_line
                 =~ $re ]]; then        echo "Usage: edit <start_line>:<end_line>"        echo
                 "Error: end_line must be a number"        return    fi    # Bash array starts
                 at 0, so let''s adjust    local start_line=$((start_line - 1))    local end_line=$((end_line))    local
                 line_count=0    local replacement=()    while IFS= read -r line    do        replacement+=("$line")        ((line_count++))    done    #
                 Create a backup of the current file    cp "$CURRENT_FILE" "/root/$(basename
                 "$CURRENT_FILE")_backup"    # Read the file line by line into an array    mapfile
                 -t lines < "$CURRENT_FILE"    local new_lines=("${lines[@]:0:$start_line}"
                 "${replacement[@]}" "${lines[@]:$((end_line))}")    # Write the new stuff
                 directly back into the original file    printf "%s\n" "${new_lines[@]}" >|
                 "$CURRENT_FILE"    # Run linter    if [[ $CURRENT_FILE == *.py ]]; then        lint_output=$(flake8
                 --isolated --select=F821,F822,F831,E111,E112,E113,E999,E902 "$CURRENT_FILE"
                 2>&1)    else        # do nothing        lint_output=""    fi    # if there
                 is no output, then the file is good    if [ -z "$lint_output" ]; then        export
                 CURRENT_LINE=$start_line        _constrain_line        _print        echo
                 "File updated. Please review the changes and make sure they are correct (correct
                 indentation, no duplicate lines, etc). Edit the file again if necessary."    else        echo
                 "Your proposed edit has introduced new syntax error(s). Please read this error
                 message carefully and then retry editing the file."        echo ""        echo
                 "ERRORS:"        _split_string "$lint_output"        echo ""        # Save
                 original values        original_current_line=$CURRENT_LINE        original_window=$WINDOW        #
                 Update values        export CURRENT_LINE=$(( (line_count / 2) + start_line
                 )) # Set to "center" of edit        export WINDOW=$((line_count + 10)) # Show
                 +/- 5 lines around edit        echo "This is how your edit would have looked
                 if applied"        echo "-------------------------------------------------"        _constrain_line        _print        echo
                 "-------------------------------------------------"        echo ""        #
                 Restoring CURRENT_FILE to original contents.        cp "/root/$(basename "$CURRENT_FILE")_backup"
                 "$CURRENT_FILE"        export CURRENT_LINE=$(( ((end_line - start_line + 1)
                 / 2) + start_line ))        export WINDOW=$((end_line - start_line + 10))        echo
                 "This is the original code before your edit"        echo "-------------------------------------------------"        _constrain_line        _print        echo
                 "-------------------------------------------------"        # Restore original
                 values        export CURRENT_LINE=$original_current_line        export WINDOW=$original_window        echo
                 "Your changes have NOT been applied. Please fix your edit command and try
                 again."        echo "You either need to 1) Specify the correct start/end line
                 arguments or 2) Correct your edit code."        echo "DO NOT re-run the same
                 failed edit command. Running it again will lead to the same error."    fi    #
                 Remove backup file    rm -f "/root/$(basename "$CURRENT_FILE")_backup"}'
               docstring: replaces lines <start_line> through <end_line> (inclusive) with the
                 given text in the open file. The replacement text is terminated by a line
                 with only end_of_edit on it. All of the <replacement text> will be entered,
                 so make sure your indentation is formatted properly. Python files will be
                 checked for syntax errors after the edit. If the system detects a syntax error,
                 the edit will not be executed. Simply try to edit the file again, but make
                 sure to read the error message and modify the edit command you issue accordingly.
                 Issuing the same command a second time will just lead to the same error message
                 again.
               end_name: end_of_edit
               name: edit
               signature: |-
                 edit <start_line>:<end_line>
                 <replacement_text>
                 end_of_edit
             _subroutines: {}
             blocklist:
             - vim
             - vi
             - emacs
             - nano
             - nohup
             - git
             blocklist_error_template: Interactive operation '{name}' is not supported by this
               environment
             blocklist_standalone:
             - python
             - python3
             - ipython
             - bash
             - sh
             - exit
             - /bin/bash
             - /bin/sh
             - nohup
             - vi
             - vim
             - emacs
             - nano
             command_docs: |+
               open:
                 docstring: opens the file at the given path in the editor. If line_number is provided, the window will be move to include that line
                 signature: open <path> [<line_number>]
                 arguments:
                   - path (string) [required]: the path to the file to open
                   - line_number (integer) [optional]: the line number to move the window to (if not provided, the window will start at the top of the file)

               goto:
                 docstring: moves the window to show <line_number>
                 signature: goto <line_number>
                 arguments:
                   - line_number (integer) [required]: the line number to move the window to

               scroll_down:
                 docstring: moves the window down {WINDOW} lines
                 signature: scroll_down

               scroll_up:
                 docstring: moves the window down {WINDOW} lines
                 signature: scroll_up

               create:
                 docstring: creates and opens a new file with the given name
                 signature: create <filename>
                 arguments:
                   - filename (string) [required]: the name of the file to create

               submit:
                 docstring: submits your current code and terminates the session
                 signature: submit

               search_dir:
                 docstring: searches for search_term in all files in dir. If dir is not provided, searches in the current directory
                 signature: search_dir <search_term> [<dir>]
                 arguments:
                   - search_term (string) [required]: the term to search for
                   - dir (string) [optional]: the directory to search in (if not provided, searches in the current directory)

               search_file:
                 docstring: searches for search_term in file. If file is not provided, searches in the current open file
                 signature: search_file <search_term> [<file>]
                 arguments:
                   - search_term (string) [required]: the term to search for
                   - file (string) [optional]: the file to search in (if not provided, searches in the current open file)

               find_file:
                 docstring: finds all files with the given name in dir. If dir is not provided, searches in the current directory
                 signature: find_file <file_name> [<dir>]
                 arguments:
                   - file_name (string) [required]: the name of the file to search for
                   - dir (string) [optional]: the directory to search in (if not provided, searches in the current directory)

               edit:
                 docstring: replaces lines <start_line> through <end_line> (inclusive) with the given text in the open file. The replacement text is terminated by a line with only end_of_edit on it. All of the
         <replacement text> will be entered, so make sure your indentation is formatted properly. Python files will be checked for syntax errors after the edit. If the system detects a syntax error, the edit will
         not be executed. Simply try to edit the file again, but make sure to read the error message and modify the edit command you issue accordingly. Issuing the same command a second time will just lead to the
         same error message again.
                 signature: edit <start_line>:<end_line>
               <replacement_text>
               end_of_edit
                 arguments:
                   - start_line (integer) [required]: the line number to start the edit at
                   - end_line (integer) [required]: the line number to end the edit at (inclusive)
                   - replacement_text (string) [required]: the text to replace the current selection with

             command_files:
             - /Users/fuchur/Documents/24/git_sync/SWE-agent/config/commands/defaults.sh
             - /Users/fuchur/Documents/24/git_sync/SWE-agent/config/commands/search.sh
             - /Users/fuchur/Documents/24/git_sync/SWE-agent/config/commands/edit_linting.sh
             - /Users/fuchur/Documents/24/git_sync/SWE-agent/config/commands/_split_string.py
             demonstration_template: |
               Here is a demonstration of how to correctly accomplish this task.
               It is included to show you how to correctly use the interface.
               You do not need to follow exactly what is done in the demonstration.
               --- DEMONSTRATION ---
               {demonstration}
               --- END OF DEMONSTRATION ---
             demonstrations:
             -
         /Users/fuchur/Documents/24/git_sync/SWE-agent/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__default__t-0.20__p-0.95__c-2.00__install-1___install_from_source/marshmallow-code__ma
         rshmallow-1867.traj
             env_variables:
               CURRENT_FILE: ''
               CURRENT_LINE: '0'
               OVERLAP: '2'
               SEARCH_FILES: ()
               SEARCH_INDEX: '0'
               SEARCH_RESULTS: ()
               WINDOW: '100'
             format_error_template: |
               Your output was not formatted correctly. You must always include one discussion and one command as part of your response. Make sure you do not have multiple discussion/command tags.
               Please make sure your output precisely matches the following format:
               DISCUSSION
               Discuss here with yourself about what your planning and what you're going to do in this step.

               ```
               command(s) that you're going to run
               ```
             history_processor: {}
             history_processor_args: {}
             instance_template: |-
               We're currently solving the following issue within our repository. Here's the issue text:
               ISSUE:
               {issue}

               INSTRUCTIONS:
               Now, you're going to solve this issue on your own. Your terminal session has started and you're in the repository's root directory. You can use any bash commands or the special interface to help
         you. Edit all the files you need to and run any checks or tests that you want.
               Remember, YOU CAN ONLY ENTER ONE COMMAND AT A TIME. You should always wait for feedback after every command.
               When you're satisfied with all of the changes you've made, you can submit your changes to the code base by simply running the submit command.
               Note however that you cannot use any interactive session commands (e.g. python, vim) in this environment, but you can write scripts and run them. E.g. you can write a python script and then run it
         with `python <script_name>.py`.

               NOTE ABOUT THE EDIT COMMAND: Indentation really matters! When editing a file, make sure to insert appropriate indentation before each line!

               IMPORTANT TIPS:
               1. Always start by trying to replicate the bug that the issues discusses.
                  If the issue includes code for reproducing the bug, we recommend that you re-implement that in your environment, and run it to make sure you can reproduce the bug.
                  Then start trying to fix it.
                  When you think you've fixed the bug, re-run the bug reproduction script to make sure that the bug has indeed been fixed.

                  If the bug reproduction script does not print anything when it successfully runs, we recommend adding a print("Script completed successfully, no errors.") command at the end of the file,
                  so that you can be sure that the script indeed ran fine all the way through.

               2. If you run a command and it doesn't work, try running a different command. A command that did not work once will not work the second time unless you modify it!

               3. If you open a file and need to get to an area around a specific line that is not in the first 100 lines, say line 583, don't just use the scroll_down command multiple times. Instead, use the
         goto 583 command. It's much quicker.

               4. If the bug reproduction script requires inputting/reading a specific file, such as buggy-input.png, and you'd like to understand how to input that file, conduct a search in the existing repo
         code, to see whether someone else has already done that. Do this by running the command: find_file "buggy-input.png" If that doesn't work, use the linux 'find' command.

               5. Always make sure to look at the currently open file and the current working directory (which appears right after the currently open file). The currently open file might be in a different
         directory than the working directory! Note that some commands, such as 'create', open files, so they might change the current  open file.

               6. When editing files, it is easy to accidentally specify a wrong line number or to write code with incorrect indentation. Always check the code after you issue an edit to make sure that it
         reflects what you wanted to accomplish. If it didn't, issue another command to fix it.

               7. It may be necessary to install the repository from source before you can run code. Please think about how to install the environment from the repository directory if you need to do so.


               (Open file: {open_file})
               (Current directory: {working_dir})
               bash-$
             next_step_no_output_template: |-
               Your command ran successfully and did not produce any output.
               (Open file: {open_file})
               (Current directory: {working_dir})
               bash-$
             next_step_template: |-
               {observation}
               (Open file: {open_file})
               (Current directory: {working_dir})
               bash-$
             parse_command: {}
             parse_function: {}
             put_demos_in_history: false
             state_command:
               arguments: null
               code: |
                 state() {
                   local working_dir="$PWD";
                   if [ -z $CURRENT_FILE ]; then
                       echo '{"open_file": "n/a", "working_dir": "'$working_dir'"}';
                   else
                       echo '{"open_file": "'$(realpath $CURRENT_FILE)'", "working_dir": "'$working_dir'"}';
                   fi
                 };
               docstring: null
               end_name: null
               name: state
               signature: null
             strategy_template: null
             submit_command: submit
             subroutine_types: []
             system_template: |-
               SETTING: You are an autonomous programmer, and you're working directly in the command line with a special interface.

               The special interface consists of a file editor that shows you {WINDOW} lines of a file at a time.
               In addition to typical bash commands, you can also use the following commands to help you navigate and edit files.

               COMMANDS:
               {command_docs}

               Please note that THE EDIT COMMAND REQUIRES PROPER INDENTATION.
               If you'd like to add the line '        print(x)' you must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and
         require fixing before it can be run.

               RESPONSE FORMAT:
               Your shell prompt is formatted as follows:
               (Open file: <path>) <cwd> $

               You need to format your output using two fields; discussion and command.
               Your output should always include _one_ discussion and _one_ command field EXACTLY as in the following example:
               DISCUSSION
               First I'll start by using ls to see what files are in the current directory. Then maybe we can look at some relevant files to see what they look like.
               ```
               ls -a
               ```

               You should only include a *SINGLE* command in the command section and then wait for a response from the shell before continuing with more discussion and commands. Everything you include in the
         DISCUSSION section will be saved for future reference.
               If you'd like to issue two commands at once, PLEASE DO NOT DO THAT! Please instead first submit just the first command, and then after receiving a response you'll be able to issue the second
         command.
               You're free to use any other bash commands you want (e.g. find, grep, cat, ls, cd) in addition to the special commands listed above.
               However, the environment does NOT support interactive session commands (e.g. python, vim), so please do not invoke them.
             util_functions:
             - arguments: null
               code: '_print() {    local total_lines=$(awk ''END {print NR}'' $CURRENT_FILE)    echo
                 "[File: $(realpath $CURRENT_FILE) ($total_lines lines total)]"    lines_above=$(jq
                 -n "$CURRENT_LINE - $WINDOW/2" | jq ''[0, .] | max | floor'')    lines_below=$(jq
                 -n "$total_lines - $CURRENT_LINE - $WINDOW/2" | jq ''[0, .] | max | round'')    if
                 [ $lines_above -gt 0 ]; then        echo "($lines_above more lines above)"    fi    cat
                 $CURRENT_FILE | grep -n $ | head -n $(jq -n "[$CURRENT_LINE + $WINDOW/2, $WINDOW/2]
                 | max | floor") | tail -n $(jq -n "$WINDOW")    if [ $lines_below -gt 0 ];
                 then        echo "($lines_below more lines below)"    fi}'
               docstring: null
               end_name: null
               name: _print
               signature: _print
             - arguments: null
               code: _constrain_line() {    if [ -z "$CURRENT_FILE" ]    then        echo "No
                 file open. Use the open command first."        return    fi    local max_line=$(awk
                 'END {print NR}' $CURRENT_FILE)    local half_window=$(jq -n "$WINDOW/2" |
                 jq 'floor')    export CURRENT_LINE=$(jq -n "[$CURRENT_LINE, $max_line - $half_window]
                 | min")    export CURRENT_LINE=$(jq -n "[$CURRENT_LINE, $half_window] | max")}
               docstring: null
               end_name: null
               name: _constrain_line
               signature: _constrain_line
           config_file: config/default_from_url.yaml
           model:
             host_url: localhost:11434
             model_name: azure:gpt4
             per_instance_cost_limit: 2.0
             replay_path: null
             temperature: 0.0
             top_p: 0.95
             total_cost_limit: 0.0
         environment:
           base_commit: null
           cache_task_images: false
           container_name: null
           data_path: https://github.com/SWE-agent/test-repo/issues/1
           environment_setup: null
           image_name: sweagent/swe-agent:latest
           install_environment: true
           no_mirror: false
           repo_path: ''
           split: dev
           timeout: null
           verbose: true
         instance_filter: .*
         print_config: true
         raise_exceptions: false
         skip_existing: true
         suffix: ''

INFO     Base commit reference None resolved to commit hash 8c179cd2be750cd9f2bb91b21adb39934311e9b8
INFO     💽 Loaded dataset from https://github.com/SWE-agent/test-repo/issues/1
INFO     Found image sweagent/swe-agent:latest with tags: ['sweagent/swe-agent:latest'], created: 2024-06-05T01:13:45.176471384Z for linux arm64.
DEBUG    Starting container with command: docker run -i --rm --name sweagent-swe-agent-latest-01edf87adc sweagent/swe-agent:latest /bin/bash -l
INFO     🌱 Environment Initialized
DEBUG    Environment initialization took 2.09 seconds
INFO     ▶️  Beginning task 0
INFO     Trying to clone from non-mirror...
WARNING  install_environment is set to True, but the data path is a GitHub URL without an environment config file (environment_config key/flag). Skipping conda environment installation.
INFO     Initializing agent settings for container 26cd13d1f31252475cde7e1ae1981d11f43e88d2066c2532611f5f2182d42737
INFO     Resetting model stats
INFO     SYSTEM (primary)
         SETTING: You are an autonomous programmer, and you're working directly in the command line with a special interface.

         The special interface consists of a file editor that shows you 100 lines of a file at a time.
         In addition to typical bash commands, you can also use the following commands to help you navigate and edit files.

         COMMANDS:
         open:
           docstring: opens the file at the given path in the editor. If line_number is provided, the window will be move to include that line
           signature: open <path> [<line_number>]
           arguments:
             - path (string) [required]: the path to the file to open
             - line_number (integer) [optional]: the line number to move the window to (if not provided, the window will start at the top of the file)

         goto:
           docstring: moves the window to show <line_number>
           signature: goto <line_number>
           arguments:
             - line_number (integer) [required]: the line number to move the window to

         scroll_down:
           docstring: moves the window down {WINDOW} lines
           signature: scroll_down

         scroll_up:
           docstring: moves the window down {WINDOW} lines
           signature: scroll_up

         create:
           docstring: creates and opens a new file with the given name
           signature: create <filename>
           arguments:
             - filename (string) [required]: the name of the file to create

         submit:
           docstring: submits your current code and terminates the session
           signature: submit

         search_dir:
           docstring: searches for search_term in all files in dir. If dir is not provided, searches in the current directory
           signature: search_dir <search_term> [<dir>]
           arguments:
             - search_term (string) [required]: the term to search for
             - dir (string) [optional]: the directory to search in (if not provided, searches in the current directory)

         search_file:
           docstring: searches for search_term in file. If file is not provided, searches in the current open file
           signature: search_file <search_term> [<file>]
           arguments:
             - search_term (string) [required]: the term to search for
             - file (string) [optional]: the file to search in (if not provided, searches in the current open file)

         find_file:
           docstring: finds all files with the given name in dir. If dir is not provided, searches in the current directory
           signature: find_file <file_name> [<dir>]
           arguments:
             - file_name (string) [required]: the name of the file to search for
             - dir (string) [optional]: the directory to search in (if not provided, searches in the current directory)

         edit:
           docstring: replaces lines <start_line> through <end_line> (inclusive) with the given text in the open file. The replacement text is terminated by a line with only end_of_edit on it. All of the
         <replacement text> will be entered, so make sure your indentation is formatted properly. Python files will be checked for syntax errors after the edit. If the system detects a syntax error, the edit will
         not be executed. Simply try to edit the file again, but make sure to read the error message and modify the edit command you issue accordingly. Issuing the same command a second time will just lead to the
         same error message again.
           signature: edit <start_line>:<end_line>
         <replacement_text>
         end_of_edit
           arguments:
             - start_line (integer) [required]: the line number to start the edit at
             - end_line (integer) [required]: the line number to end the edit at (inclusive)
             - replacement_text (string) [required]: the text to replace the current selection with



         Please note that THE EDIT COMMAND REQUIRES PROPER INDENTATION.
         If you'd like to add the line '        print(x)' you must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and
         require fixing before it can be run.

         RESPONSE FORMAT:
         Your shell prompt is formatted as follows:
         (Open file: <path>) <cwd> $

         You need to format your output using two fields; discussion and command.
         Your output should always include _one_ discussion and _one_ command field EXACTLY as in the following example:
         DISCUSSION
         First I'll start by using ls to see what files are in the current directory. Then maybe we can look at some relevant files to see what they look like.
         ```
         ls -a
         ```

         You should only include a *SINGLE* command in the command section and then wait for a response from the shell before continuing with more discussion and commands. Everything you include in the DISCUSSION
         section will be saved for future reference.
         If you'd like to issue two commands at once, PLEASE DO NOT DO THAT! Please instead first submit just the first command, and then after receiving a response you'll be able to issue the second command.
         You're free to use any other bash commands you want (e.g. find, grep, cat, ls, cd) in addition to the special commands listed above.
         However, the environment does NOT support interactive session commands (e.g. python, vim), so please do not invoke them.
INFO     DEMONSTRATION:
         /Users/fuchur/Documents/24/git_sync/SWE-agent/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__default__t-0.20__p-0.95__c-2.00__install-1___install_from_source/marshmallow-code__ma
         rshmallow-1867.traj
INFO     Trajectory will be saved to trajectories/fuchur/azure-gpt4__SWE-agent__test-repo__default_from_url__t-0.00__p-0.95__c-2.00__install-1/SWE-agent__test-repo-i1.traj
INFO     🤖 MODEL INPUT
         We're currently solving the following issue within our repository. Here's the issue text:
         ISSUE:
         SyntaxError: invalid syntax
         I'm running `missing_colon.py` as follows:

         ```python
         division(23, 0)
         ```

         but I get the following error:

         ```
           File "/Users/fuchur/Documents/24/git_sync/swe-agent-test-repo/tests/./missing_colon.py", line 4
             def division(a: float, b: float) -> float
                                                      ^
         SyntaxError: invalid syntax
         ```


         INSTRUCTIONS:
         Now, you're going to solve this issue on your own. Your terminal session has started and you're in the repository's root directory. You can use any bash commands or the special interface to help you.
         Edit all the files you need to and run any checks or tests that you want.
         Remember, YOU CAN ONLY ENTER ONE COMMAND AT A TIME. You should always wait for feedback after every command.
         When you're satisfied with all of the changes you've made, you can submit your changes to the code base by simply running the submit command.
         Note however that you cannot use any interactive session commands (e.g. python, vim) in this environment, but you can write scripts and run them. E.g. you can write a python script and then run it with
         `python <script_name>.py`.

         NOTE ABOUT THE EDIT COMMAND: Indentation really matters! When editing a file, make sure to insert appropriate indentation before each line!

         IMPORTANT TIPS:
         1. Always start by trying to replicate the bug that the issues discusses.
            If the issue includes code for reproducing the bug, we recommend that you re-implement that in your environment, and run it to make sure you can reproduce the bug.
            Then start trying to fix it.
            When you think you've fixed the bug, re-run the bug reproduction script to make sure that the bug has indeed been fixed.

            If the bug reproduction script does not print anything when it successfully runs, we recommend adding a print("Script completed successfully, no errors.") command at the end of the file,
            so that you can be sure that the script indeed ran fine all the way through.

         2. If you run a command and it doesn't work, try running a different command. A command that did not work once will not work the second time unless you modify it!

         3. If you open a file and need to get to an area around a specific line that is not in the first 100 lines, say line 583, don't just use the scroll_down command multiple times. Instead, use the goto 583
         command. It's much quicker.

         4. If the bug reproduction script requires inputting/reading a specific file, such as buggy-input.png, and you'd like to understand how to input that file, conduct a search in the existing repo code, to
         see whether someone else has already done that. Do this by running the command: find_file "buggy-input.png" If that doesn't work, use the linux 'find' command.

         5. Always make sure to look at the currently open file and the current working directory (which appears right after the currently open file). The currently open file might be in a different directory
         than the working directory! Note that some commands, such as 'create', open files, so they might change the current  open file.

         6. When editing files, it is easy to accidentally specify a wrong line number or to write code with incorrect indentation. Always check the code after you issue an edit to make sure that it reflects what
         you wanted to accomplish. If it didn't, issue another command to fix it.

         7. It may be necessary to install the repository from source before you can run code. Please think about how to install the environment from the repository directory if you need to do so.


         (Open file: n/a)
         (Current directory: /SWE-agent__test-repo)
         bash-$
INFO     input_tokens=10,194, output_tokens=56, instance_cost=0.10, cost=0.10
INFO     total_tokens_sent=10,194, total_tokens_received=56, total_cost=0.10, total_api_calls=1
INFO     💭 THOUGHT (primary)
         First, let's locate the file `missing_colon.py` mentioned in the issue to understand the syntax error and fix it. We'll use the `find_file` command to search for this file in the current directory.

INFO     🎬 ACTION (primary)
         find_file "missing_colon.py"

INFO     🤖 MODEL INPUT
         Found 1 matches for "missing_colon.py" in /SWE-agent__test-repo:
         /SWE-agent__test-repo/tests/missing_colon.py
         (Open file: n/a)
         (Current directory: /SWE-agent__test-repo)
         bash-$
INFO     input_tokens=10,314, output_tokens=45, instance_cost=0.21, cost=0.10
INFO     total_tokens_sent=20,508, total_tokens_received=101, total_cost=0.21, total_api_calls=2
INFO     💭 THOUGHT (primary)
         Now that we've located the file `missing_colon.py` in the `tests` directory, let's open it to inspect the syntax error mentioned in the issue.

INFO     🎬 ACTION (primary)
         open tests/missing_colon.py

INFO     🤖 MODEL INPUT
         [File: /SWE-agent__test-repo/tests/missing_colon.py (10 lines total)]
         1:#!/usr/bin/env python3
         2:
         3:
         4:def division(a: float, b: float) -> float
         5:    return a/b
         6:
         7:
         8:if __name__ == "__main__":
         9:    print(division(123, 15))
         10:
         (Open file: /SWE-agent__test-repo/tests/missing_colon.py)
         (Current directory: /SWE-agent__test-repo)
         bash-$
INFO     input_tokens=10,484, output_tokens=59, instance_cost=0.31, cost=0.11
INFO     total_tokens_sent=30,992, total_tokens_received=160, total_cost=0.31, total_api_calls=3
INFO     💭 THOUGHT (primary)
         The syntax error is due to the missing colon at the end of the function definition on line 4. Let's add the missing colon to correct the syntax error.

INFO     🎬 ACTION (primary)
         edit 4:4
         def division(a: float, b: float) -> float:
         end_of_edit

INFO     🤖 MODEL INPUT
         [File: /SWE-agent__test-repo/tests/missing_colon.py (10 lines total)]
         1:#!/usr/bin/env python3
         2:
         3:
         4:def division(a: float, b: float) -> float:
         5:    return a/b
         6:
         7:
         8:if __name__ == "__main__":
         9:    print(division(123, 15))
         10:
         File updated. Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.
         (Open file: /SWE-agent__test-repo/tests/missing_colon.py)
         (Current directory: /SWE-agent__test-repo)
         bash-$
INFO     input_tokens=10,698, output_tokens=47, instance_cost=0.42, cost=0.11
INFO     total_tokens_sent=41,690, total_tokens_received=207, total_cost=0.42, total_api_calls=4
INFO     💭 THOUGHT (primary)
         The syntax error has been corrected by adding the missing colon at the end of the function definition. Now, let's run the script to ensure that it executes without any syntax errors.

INFO     🎬 ACTION (primary)
         python tests/missing_colon.py

INFO     🤖 MODEL INPUT
         8.2
         (Open file: /SWE-agent__test-repo/tests/missing_colon.py)
         (Current directory: /SWE-agent__test-repo)
         bash-$
INFO     input_tokens=10,790, output_tokens=45, instance_cost=0.53, cost=0.11
INFO     total_tokens_sent=52,480, total_tokens_received=252, total_cost=0.53, total_api_calls=5
INFO     💭 THOUGHT (primary)
         The script executed successfully and printed the result of the division function, which confirms that the syntax error has been resolved. Now that the issue is fixed, we can submit the changes to the
         code base.

INFO     🎬 ACTION (primary)
         submit

INFO     Found submission:
         diff --git a/tests/missing_colon.py b/tests/missing_colon.py
         index 20edef5..5857437 100755
         --- a/tests/missing_colon.py
         +++ b/tests/missing_colon.py
         @@ -1,7 +1,7 @@
          #!/usr/bin/env python3


         -def division(a: float, b: float) -> float
         +def division(a: float, b: float) -> float:
              return a/b



INFO     Trajectory saved to trajectories/fuchur/azure-gpt4__SWE-agent__test-repo__default_from_url__t-0.00__p-0.95__c-2.00__install-1/SWE-agent__test-repo-i1.traj
INFO     Saved predictions to trajectories/fuchur/azure-gpt4__SWE-agent__test-repo__default_from_url__t-0.00__p-0.95__c-2.00__install-1/all_preds.jsonl
╭──────────────────────────── 🎉 Submission successful 🎉 ────────────────────────────╮
 SWE-agent has produced a patch that it believes will solve the issue you submitted! 
 Use the code snippet below to inspect or apply it!                                  
╰─────────────────────────────────────────────────────────────────────────────────────╯

 # The patch has been saved to your local filesystem at:
 PATCH_FILE_PATH='/Users/fuchur/Documents/24/git_sync/SWE-agent/trajectories/fuchur/azure-gpt4__SWE-agent__test-repo__default_from_url__t-0.00__p-0.95__c-2.00__install-1/patches/SWE-agent__test-repo-i1.patch'
 # Inspect it:
 cat "${PATCH_FILE_PATH}"
 # Apply it to a local repository:
 cd <your local repo root>
 git apply "${PATCH_FILE_PATH}"

Here,

  • --model_name sets the language model that is used by SWE-agent (with gpt4 being the default). More information on the available models in our FAQ
  • --data_path points to the source of the problem statement (for example, the GitHub issue that you want to solve). You can also point it to local files (see below)
  • --config_file includes settings such as the prompts. Changing the config file is the easiest way to get started with modifying SWE-agent (more advanced options are discussed here).
  • --per_instance_cost_limit limits the total inference cost to $2 (default is $3).

All options

Run python run.py --help to see all available options for run.py. This tutorial will only cover a subset of options.

Running more than once

  • The complete details of the run are saved as a "trajectory" file (more about them here). They can also be turned into new demonstrations.
  • If you run the same command more than once, you will find that SWE-agent aborts with Skipping existing trajectory. You can either remove the trajectory from the warning message, or add the --skip_existing=False flag.
  • If you solve multiple issues from the same repository/in the same environment, you can specify the --cache_task_images flag. This will create a persistent docker image with the initialized environment required for the problem.

Specifying the repository

Operating in batch mode: Running on SWE-bench and other benchmark sets

If you want to run SWE-agent in batch mode on SWE-bench or another whole evaluation set, see benchmarking. This tutorial focuses on using SWE-agent on individual issues.

In the above example, the repository/codebase is inferred from the --data_path. This options is currently only available for GitHub issues. For all other use cases, you can specify --repo_path, which accepts either GitHub URLs or paths to local repositories.

To try it out, let's clone the test repository from the previous section.

git clone git@github.com:SWE-agent/test-repo.git

and then run

python run.py \
  --data_path /path/to/test-repo/problem_statements/1.md \
  --repo_path /path/to/test-repo \
  --config_file config/default_from_url.yaml \
  --apply_patch_locally

where you replaced paths with the prefix /path/to/.../ with the actual paths to the corresponding file/directory.

We have also added a new flag, --apply_patch_locally, which will make SWE-agent apply the changes to the local repository (if it believes that it has successfully solved the issue).

You can mix and match the different ways of specifying problem statements and repositories. For example, any of the following combination of options also works

  • Local problem statement with GitHub repository (--data_path /path/to/problem.md --repo_path https://github.com/...): Let SWE-agent work on something that wasn't reported yet
  • GitHub issue with local repository (--data_path https://github.com/.../issues/.. --repo_path /path/to/... --apply_patch_locally): Let SWE-agent solve a GitHub issue locally (for example to edit the solution afterwards)
  • GitHub issue with different GitHub repository: Useful with the --open_pr flag (see below) when working from a fork.

In addition, if --repo_path points to a GitHub repository, you can use --base_commit to specify

  • A branch name (e.g., dev),
  • A tag (e.g., v1.0.0),
  • A commit hash (e.g., a4464baca1f28d7733337df6e4daa6c1ed920336).

SWE-agent will then start from this commit when trying to solve the problem.

Uncommitted changes

When running with a local --repo_path, SWE-agent will use the last commit, i.e., all local, uncommitted changes will not be seen by SWE-agent.

Installing dependencies and setting up the environment

Now let's move on to a slightly more complicated issue (swe-agent/test-repo #22).

What makes it more complicated? This time the problematic code is part of a library testpkg, so SWE-agent first has to install the package in order to reproduce the issue before searching for the problematic code.

In most circumstances, GPT4 will attempt to install the package and requirements (usually with some form of pip install . or pip install pkg). However, this wastes valuable queries to the LM. In addition, you might need to run your software for a specific python version or have other specific environment settings. The --environment_setup flag is used to fix this problem.

Let's try it:

python run.py \
  --data_path https://github.com/SWE-agent/test-repo/issues/22 \
  --config_file config/default_from_url.yaml \
  --environment_setup config/environment_setup/py310_default.yaml

This time, pip install -e . is called before SWE-agent gets to work, installing the package defined in the repository.

Let's take a look at the py310_default.yaml config file

python: '3.10'
# Use uv pip for speedup, but fallback to classic pip if we fail
# Upgrade pip to avoid https://stackoverflow.com/a/73779542/
install: 'uv pip install -e . || (python -m pip install --upgrade pip && python -m pip install -e .)'

Here, install is an arbitrary command that is run, while python will be the required python version. The default install command will create an editable install of the python package. We first try to use uv pip (a much faster implementation of pip in rust), but fall back to "normal" pip if it fails.

Editable installs

Using editable installs is crucial for SWE-agent so that changes to the package code take effect without having to reinstall the package.

The config file can have the following keys:

  • python: Python version (will be set up via conda)
  • packages: Either requirements.txt, environment.yml (finds the corresponding file and installs from there) or a whitespace separated list of conda packages
  • pip_packages: A list of additional python packages that are installed with pip install PACKAGE
  • pre_install: A list of custom commands
  • install: A custom command
  • post_install: A list of custom commands

If you have very specific requirements, that can not be installed via conda, you can create a custom Docker image.

Speeding up SWE-agent

Speed up in v0.6

SWE-agent v0.6 (June 4th, 2024) introduced major speedups. Please upgrade to the latest version. To make use of uv pip, make sure that you have the latest sweagent/swe-agent:latest image.

After the Docker container has been started, the target repository cloned/copied, and the dependencies installed, almost all of the remaining runtime can be attributed to waiting for your LM provider to answer your API calls.

Therefore, speeding SWE-agent is mostly about speeding up the setup stages. We currently offer three ways to cache the setup stages:

  • By specifying --container_name, you run SWE-agent with a persistent Docker container: Rather than being deleted after every task, the Docker container will only be paused and can be resumed. Cloned repositories from previous runs with the same container name, as well as any installed conda environments (versioned by the version of the package you are installing) will be already available.
  • Alternatively, you can specify --cache_task_images. For every repository/base commit/environment setup, we commit the changes from the installation stage to the Docker image. The corresponding containers are temporary as usual. Unlike the persistent containers, there will be a new image for almost every base commit (that is, probably for every task when evaluating on a benchmark), which makes this only relevant when running over the same tasks more than once (for example when testing different agent configurations or LMs).
  • You can also build your own Docker image and ensure that all relevant conda environments and repositories are available (check the logs from the previous runs to get the names for repositories and environments).

Confused about the two options?

Probably --container_name my_container_name will do what you want.

What's the difference between Docker images and containers?

Docker containers are running instances of Docker images (that you can think of as snapshots of what happens after you build the Dockerfile). More information.

Taking actions

  • As mentioned above, you can use --apply_patch_locally to have SWE-agent apply successful solution attempts to local files.
  • Alternatively, when running on a GitHub issue, you can have the agent automatically open a PR if the issue has been solved by supplying the --open_pr flag. Please use this feature responsibly (on your own repositories or after careful consideration).

Alternatively, you can always retrieve the patch that was generated by SWE-agent. Watch out for the followoing message in the log:

╭──────────────────────────── 🎉 Submission successful 🎉 ────────────────────────────╮
│ SWE-agent has produced a patch that it believes will solve the issue you submitted! │
│ Use the code snippet below to inspect or apply it!                                  │
╰─────────────────────────────────────────────────────────────────────────────────────╯

And follow the instructions below it:

 # The patch has been saved to your local filesystem at:
 PATCH_FILE_PATH='/Users/.../patches/05917d.patch'
 # Inspect it:
 cat "${PATCH_FILE_PATH}"
 # Apply it to a local repository:
 cd <your local repo root>
 git apply "${PATCH_FILE_PATH}"