├── .github └── workflows │ └── publish.yml ├── .gitignore ├── CONTRIBUTE.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── assets ├── image-1.png ├── image-2.png └── image.png ├── exampleEnv ├── bin │ ├── activate │ ├── env_setter.sh │ ├── interceptor.sh │ └── nlt_end └── static │ └── os_info.json ├── execution_permissions.log ├── natural_language_terminal ├── .DS_Store ├── __init__.py ├── __main__.py ├── cli │ ├── __init__.py │ └── app.py ├── core │ └── system.py ├── create_env.py ├── git │ └── autocommit.py ├── prompts.py ├── shell_scripts │ ├── activate_template.bat │ ├── activate_template.sh │ ├── end_template.bat │ ├── end_template.sh │ ├── env_setter.sh │ └── interceptor.sh └── terminal │ ├── base.py │ └── utils.py ├── nlt.png ├── nlt.py ├── requirements.txt └── setup.py /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python 🐍 distributions 📦 to PyPI 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | build-n-publish: 10 | name: Build and publish Python 🐍 distributions 📦 to PyPI 11 | runs-on: ubuntu-18.04 12 | steps: 13 | - uses: actions/checkout@master 14 | - name: Set up Python 3.10 15 | uses: actions/setup-python@v3 16 | with: 17 | python-version: '3.10' 18 | - name: Install pypa/setuptools 19 | run: >- 20 | python -m 21 | pip install wheel 22 | - name: Extract tag name 23 | id: tag 24 | run: echo ::set-output name=TAG_NAME::$(echo $GITHUB_REF | cut -d / -f 3) 25 | - name: Update version in setup.py 26 | run: >- 27 | sed -i "s/{{VERSION_PLACEHOLDER}}/${{ steps.tag.outputs.TAG_NAME }}/g" setup.py 28 | - name: Build a binary wheel 29 | run: >- 30 | python setup.py sdist bdist_wheel 31 | - name: Publish distribution 📦 to PyPI 32 | uses: pypa/gh-action-pypi-publish@master 33 | with: 34 | password: ${{ secrets.PYPI_API_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | venv 4 | __pycache__ 5 | .env 6 | 7 | node_modules 8 | natural_language_terminal.egg-info 9 | 10 | myenv 11 | test3 12 | session 13 | 14 | test.sh 15 | test.bat 16 | 17 | test1.bat 18 | setup_git_alias.bat 19 | 20 | dist 21 | -------------------------------------------------------------------------------- /CONTRIBUTE.md: -------------------------------------------------------------------------------- 1 | # How to contribute to NLT 2 | Thank you for your interest in NLT! 3 | Here's a guide to help you contribute to this project. 4 | 5 | ## 1. Get Started 6 | ### Fork the repository 7 | 8 | At first, you need to fork this copy and create your own version of repo. 9 | 10 | ### Clone the repository and install the dependencies. 11 | 12 | ### Installing dependencies with pip 13 | ```bash 14 | pip install -r requirements.txt 15 | ``` 16 | 17 | ### Installing pre-commit 18 | We strongly recommend installing [pre-commit](https://pre-commit.com/) to ensure proper formatting during development 19 | 20 | ## 2. Developing and Testing 21 | ### Create a branch 22 | 23 | Create a new branch for developing your creative features 24 | 25 | ```shell 26 | git checkout -b your-feature 27 | ``` 28 | 29 | ### Make changes and testing 30 | 31 | You can develop new features and then you need to make sure everything works as expected. Run our provided tests and make sure the existing ones go well. Your new tests are encouraged. 32 | 33 | ## 3. Submitting Changes 34 | 35 | ### Code format check 36 | Please ensure your code is formatted correctly using pre-commit 37 | 38 | ### Git commit format 39 | We strongly recommend your git commit follows the format below 40 | ```bash 41 | git commit -m : 42 | ``` 43 | 44 | | | | 45 | |-------------|--------------------------------------------------| 46 | | `feat` | Add new features | 47 | | `fix` | Fix bugs | 48 | | `docs` | Modify documents like README, CONTRIBUTE | 49 | | `style` | Modify code format like space and comma without changing code logic | 50 | | `refactor` | Refactor code structure without adding new features or fixing new bugs | 51 | | `perf` | Improve performance or user experience | 52 | | `test` | Test features, including unit test and integration test | 53 | | `chore` | Change the build procedure or add dependencies | 54 | | `revert` | Revert to the previous version | 55 | 56 | ### Create a Pull Request 57 | 58 | 1. Visit your forked NaturalLanguageTerminal repository on GitHub and click the "Compare & pull request" button to initiate the process of submitting your changes to the original repository for review and potential merging. 59 | 2. Choose the base branch and the compare branch (your feature branch).💡 Note that when you add new features, it is recommended to choose the (`dev`) branch and if your change does not affect original functions, you may consider choosing the (`main`) branch. 60 | 3. Write a title and describe your changes in the description. And it is recommended to select the label of the change to make it more clear. 61 | 62 | 63 | ## 4. Style Guide 64 | 65 | This is the style guide determining how code should be formatted. As more code is added, this documentation will be updated. 66 | 67 | ### Line Length 68 | 69 | Each line should not exceed 80 characters. 70 | 71 | ### Spaces 72 | Each function or class is to have a space between the line and any preceding code if it is not directly associated with the function or class. For example: 73 | ```py 74 | # this is a useful comment for the bar function 75 | 76 | def foo(): 77 | pass 78 | ``` 79 | 80 | ## 5. Review and Approval 81 | Our maintainers will have a review of that and might give some suggestions or ask for more details. After they approve, your commitment can be incorporated into NLT! 82 | 83 | If you need some ideas on what to get started with, take a look at our goals for the rest of this year in [issues](https://github.com/PathOnAI/NaturalLanguageTerminal/issues). -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Path On AI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | recursive-include natural_language_terminal * 3 | recursive-include natural_language_terminal/shell_scripts * -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Natural Language Terminal (NLT) 2 | repo owner: Balaji Rama (balajirw10@gmail.com) 3 | 4 |

5 | Welcome to the future of command-line interfaces. 6 |

7 | 8 |

9 | Natural Language Terminal (NLT) revolutionizes the way you interact with your system, bringing the power of natural language processing to your fingertips. 10 |

11 | 12 |

13 | Overview • 14 | Features • 15 | Installation • 16 | Usage • 17 | Plugins • 18 | Contributing • 19 | License 20 |

21 | 22 | --- 23 | 24 |

Note: We currently only support MacOS. Windows compatibility is still under development.

25 | 26 | ## 📦 1. Get Started 27 | ### 1.1 Prerequisite 28 | Install Xcode from the Mac App Store: 29 | 30 | Open the Mac App Store 31 | Search for "Xcode" 32 | Click "Get" or "Install" 33 | The download might take a while as Xcode is a large application (several GB) 34 | 35 | 36 | Once Xcode is fully installed, then try running the command again: 37 | ```bash 38 | sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer 39 | ``` 40 | 41 | ### 1.2 Python Setup 42 | This project is developed using Python 3.10. Follow these steps to set up your environment on Mac: 43 | 1. Install Python 3.10 using Homebrew: 44 | ```bash 45 | brew install python@3.10 46 | ``` 47 | 48 | 2. Link Python 3.10: 49 | ```bash 50 | brew link python@3.10 51 | ``` 52 | 53 | 3. Add Python aliases to your shell configuration: 54 | ```bash 55 | echo 'alias python="/opt/homebrew/bin/python3.10"' >> ~/.zshrc 56 | echo 'alias python3="/opt/homebrew/bin/python3.10"' >> ~/.zshrc 57 | ``` 58 | 59 | 4. Apply the changes: 60 | ```bash 61 | source ~/.zshrc 62 | ``` 63 | 64 | 5. Verify your Python installation: 65 | ```bash 66 | python --version # Should show Python 3.10.x 67 | python3 --version # Should show Python 3.10.x 68 | ``` 69 | ### 1.3 Installing Project Dependencies 70 | 71 | 1. Install the natural-language-terminal package from source: 72 | ```bash 73 | python -m pip install -e . 74 | ``` 75 | 76 | 2. Install required system utilities: 77 | ```bash 78 | python -m pip install psutil 79 | python -m pip install requests 80 | ``` 81 | 82 | 83 | ## 2 Usage 84 | ### 2.1 NL session initialization 85 | ```bash 86 | nlt init 87 | ``` 88 | 89 | ### 2.2 Creating a New Environment 90 | 91 | ```bash 92 | nlt create newEnv 93 | ``` 94 | After running this command, a folder called newEnv will be created. We have uploaded the exampleEnv folder as an example for reference. See the [exampleEnv](./exampleEnv) subfolder. 95 | 96 | ### 2.3 Activating an Environment 97 | 98 | 99 | ```bash 100 | source newEnv/bin/activate 101 | ``` 102 |

103 | 104 |

105 | 106 | ### 2.4 Deactivating 107 | 108 | For both macOS and Windows: 109 | 110 | ```bash 111 | remove 112 | ``` 113 |

114 | 115 |

116 | 117 | ### 2.5 get NLT suggestions 118 | Now you can chat with the terminal to receive command suggestions and choose whether to run them. 119 |

120 | 121 |

122 | 123 | 124 | ## 🤝 4. Contributing 125 | 126 | We welcome contributions! Please see our [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to get started. 127 | 128 | [![NaturalLanguageTerminal contributors](https://contrib.rocks/image?repo=PathOnAI/NaturalLanguageTerminal)](https://github.com/PathOnAI/NaturalLanguageTerminal/graphs/contributors) 129 | 130 | 131 | --- 132 | 133 |

134 | "Speak to your terminal, and it shall listen." 135 |

136 | 137 | 138 | ## Star History 139 | 140 | [![Star History Chart](https://api.star-history.com/svg?repos=PathOnAI/NaturalLanguageTerminal&type=Date)](https://star-history.com/#PathOnAI/NaturalLanguageTerminal&Date) 141 | -------------------------------------------------------------------------------- /assets/image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PathOnAIOrg/NaturalLanguageTerminal/ba74875b254a38d7ea41a1fb7bec075730701f99/assets/image-1.png -------------------------------------------------------------------------------- /assets/image-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PathOnAIOrg/NaturalLanguageTerminal/ba74875b254a38d7ea41a1fb7bec075730701f99/assets/image-2.png -------------------------------------------------------------------------------- /assets/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PathOnAIOrg/NaturalLanguageTerminal/ba74875b254a38d7ea41a1fb7bec075730701f99/assets/image.png -------------------------------------------------------------------------------- /exampleEnv/bin/activate: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the directory of the current script 4 | FILE_DIR=$(dirname "$(readlink -f "$0")") 5 | 6 | echo "FILE_DIR is set to: $FILE_DIR" 7 | 8 | # Save the current PATH and PS1 9 | export OLD_PATH="$PATH" 10 | export OLD_PS1="$PS1" 11 | 12 | # Add the script directory to PATH 13 | export PATH="$FILE_DIR:$PATH" 14 | 15 | # Save the current Python interpreter path 16 | export nlt_PYTHON_PATH="/opt/homebrew/bin/python3.10" 17 | 18 | # Confirm Python path 19 | echo "nlt_PYTHON_PATH is set to: $nlt_PYTHON_PATH" 20 | 21 | # Create the git-autocommit alias using the full path to Python 22 | git config --global alias.autocommit "!$nlt_PYTHON_PATH -c \"from natural_language_terminal.git.autocommit import main; main()\"" 23 | 24 | alias remove="source $FILE_DIR/nlt_end" 25 | 26 | source "$FILE_DIR/interceptor.sh" 27 | 28 | 29 | # Before eval "intercept" 30 | echo "About to run intercept function" 31 | 32 | intercept 33 | echo "Intercept function executed" 34 | 35 | # Set a flag to indicate nlt is active 36 | export nlt_ACTIVE=1 37 | 38 | source "$FILE_DIR/env_setter.sh" 39 | 40 | if [ -n "$BASH_VERSION" ]; then 41 | # Navy blue color code for Bash 42 | navy_blue="\[\e[38;5;17m\]" 43 | reset_color="\[\e[0m\]" 44 | 45 | # Modify PS1 for Bash 46 | export PS1="${navy_blue}[nlt~exampleEnv]${reset_color} $PS1" 47 | elif [ -n "$ZSH_VERSION" ]; then 48 | # Navy blue color code for Zsh 49 | navy_blue="%F{17}" 50 | reset_color="%f" 51 | 52 | # Modify PROMPT for Zsh 53 | export PROMPT="${navy_blue}[nlt~exampleEnv]${reset_color} $PROMPT" 54 | fi 55 | 56 | # ANSI color codes 57 | GREEN='\033[0;32m' 58 | CYAN='\033[0;36m' 59 | YELLOW='\033[1;33m' 60 | NC='\033[0m' # No Color 61 | 62 | # Print stylized activation message 63 | echo -e "\n${GREEN}┌────────────────────────────────────────────┐${NC}" 64 | echo -e "${GREEN}│ │${NC}" 65 | echo -e "${GREEN}│ ${YELLOW}nlt environment activated successfully${NC} ${GREEN}│${NC}" 66 | echo -e "${GREEN}│ │${NC}" 67 | echo -e "${GREEN}├────────────────────────────────────────────┤${NC}" 68 | echo -e "${GREEN}│ │${NC}" 69 | echo -e "${GREEN}│${NC} Environment: ${CYAN}exampleEnv${NC} ${GREEN}│${NC}" 70 | echo -e "${GREEN}│ ${NC}To end the session, type: ${CYAN}remove${NC} ${GREEN}│${NC}" 71 | echo -e "${GREEN}│ │${NC}" 72 | echo -e "${GREEN}└────────────────────────────────────────────┘${NC}\n" 73 | -------------------------------------------------------------------------------- /exampleEnv/bin/env_setter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Path to the config file 4 | CONFIG_FILE="$HOME/.nltconfig.json" 5 | 6 | # Check if the config file exists 7 | if [ ! -f "$CONFIG_FILE" ]; then 8 | echo "Error: Config file not found at $CONFIG_FILE" 9 | exit 1 10 | fi 11 | 12 | # Check if jq is installed 13 | if ! command -v jq &> /dev/null; then 14 | echo "Error: jq is not installed. Please install it first." 15 | exit 1 16 | fi 17 | 18 | # Read API keys from the config file and export them 19 | jq -r '.API_KEYS | to_entries[] | "\(.key)=\(.value)"' "$CONFIG_FILE" | while IFS= read -r line; do 20 | # Skip empty lines 21 | [ -z "$line" ] && continue 22 | 23 | # Split the line into key and value 24 | key="${line%%=*}" 25 | value="${line#*=}" 26 | 27 | # Skip null values and only process API_KEY entries 28 | if [ "$value" != "null" ] && [[ $key == *"API_KEY"* ]]; then 29 | # Remove any surrounding quotes from the value 30 | value="${value%\"}" 31 | value="${value#\"}" 32 | 33 | # Export the environment variable 34 | export "$key=$value" 35 | echo "Exported: $key" 36 | fi 37 | done 38 | 39 | -------------------------------------------------------------------------------- /exampleEnv/bin/interceptor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script works for both Bash and Zsh 3 | 4 | # Variable to store intercepted commands 5 | INTERCEPTED_COMMAND="" 6 | 7 | # Variable to control interception state 8 | INTERCEPTION_ACTIVE=false 9 | 10 | # Function to log the command 11 | log_command() { 12 | local cmd="$1" 13 | echo "Command intercepted: $cmd" >> ~/.intercepted_commands.log 14 | } 15 | 16 | # Function to be used as a trap 17 | intercept_command() { 18 | if $INTERCEPTION_ACTIVE; then 19 | # Get the command that's about to be executed 20 | local cmd="$BASH_COMMAND" 21 | 22 | if [[ -n "$ZSH_VERSION" ]]; then 23 | cmd="$1" 24 | # trap - DEBUG 25 | fi 26 | 27 | # Ignore specific commands 28 | case "$cmd" in 29 | intercept|last_intercepted|toggle_interception|show_last_intercepted) 30 | return 0 31 | ;; 32 | esac 33 | 34 | # Store and log the command 35 | INTERCEPTED_COMMAND="$cmd" 36 | log_command "$INTERCEPTED_COMMAND" 37 | 38 | # Prevent the command from executing 39 | if [[ -n "$ZSH_VERSION" ]]; then 40 | return 1 41 | else 42 | kill -INT $$ 43 | fi 44 | fi 45 | } 46 | 47 | # Function to toggle command interception 48 | toggle_interception() { 49 | if $INTERCEPTION_ACTIVE; then 50 | INTERCEPTION_ACTIVE=false 51 | if [[ -n "$ZSH_VERSION" ]]; then 52 | if (( ${#preexec_functions[@]} )); then 53 | preexec_functions=("${preexec_functions[@]:#intercept_command}") 54 | fi 55 | else 56 | trap - DEBUG 57 | fi 58 | else 59 | INTERCEPTION_ACTIVE=true 60 | if [[ -n "$ZSH_VERSION" ]]; then 61 | if ! (( ${preexec_functions[(Ie)intercept_command]} )); then 62 | preexec_functions+=(intercept_command) 63 | fi 64 | else 65 | trap 'intercept_command' DEBUG 66 | fi 67 | fi 68 | } 69 | 70 | # Alias to easily toggle interception 71 | alias intercept='toggle_interception' 72 | 73 | # Function to show the last intercepted command 74 | show_last_intercepted() { 75 | if [[ -n "$INTERCEPTED_COMMAND" ]]; then 76 | echo "Last intercepted command: $INTERCEPTED_COMMAND" 77 | else 78 | echo "No command has been intercepted yet." 79 | fi 80 | } 81 | 82 | # Alias to show last intercepted command 83 | alias last_intercepted='show_last_intercepted' 84 | 85 | # zsh specific handler 86 | command_not_found_handler() { 87 | # Add a guard against recursion 88 | if [[ -n "$IN_COMMAND_HANDLER" ]]; then 89 | echo "zsh: command not found: $*" 90 | return 1 91 | fi 92 | 93 | export IN_COMMAND_HANDLER=1 94 | 95 | if $INTERCEPTION_ACTIVE; then 96 | ai_message_generator "$*" 97 | local result=$? 98 | unset IN_COMMAND_HANDLER 99 | return $result 100 | else 101 | echo "zsh: command not found: $*" 102 | unset IN_COMMAND_HANDLER 103 | return 1 104 | fi 105 | } 106 | 107 | ai_message_generator() { 108 | local cmd="$*" 109 | 110 | eval "$nlt_PYTHON_PATH -c \"from natural_language_terminal.terminal.base import main; main('$cmd')\"" 111 | 112 | if [ $? -ne 0 ]; then 113 | echo "Error occurred while processing the API response." 114 | return 1 115 | fi 116 | 117 | filename="execution_permissions.log" 118 | 119 | # Check if the file exists 120 | if [ ! -f "$filename" ]; then 121 | echo "Error: File '$filename' not found" 122 | exit 1 123 | fi 124 | 125 | # Read the first two lines from the file 126 | read -r line1 < "$filename" 127 | read -r line2 < <(sed -n '2p' "$filename") 128 | 129 | # Convert line1 to lowercase for case-insensitive comparison 130 | line1_lower=$(echo "$line1" | tr '[:upper:]' '[:lower:]') 131 | 132 | # Check if the first line is 'y' 133 | if [ "$line1_lower" = "y" ]; then 134 | eval "$line2" 135 | else 136 | echo "No command executed" 137 | fi 138 | 139 | return 0 140 | } 141 | -------------------------------------------------------------------------------- /exampleEnv/bin/nlt_end: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Unset the git alias 4 | git config --global --unset alias.autocommit 5 | 6 | # Restore the old PATH and PS1 7 | export PATH="$OLD_PATH" 8 | export PS1="$OLD_PS1" 9 | 10 | #end interceptor 11 | eval "intercept" 12 | 13 | # Unset nlt-specific environment variables 14 | unset nlt_ACTIVE 15 | unset nlt_PYTHON_PATH 16 | unset OLD_PATH 17 | unset OLD_PS1 18 | 19 | # ANSI color codes 20 | RED='\033[0;31m' 21 | NC='\033[0m' # No Color 22 | 23 | # Print stylized deactivation message 24 | echo -e "\n${RED}┌────────────────────────────────────────────┐${NC}" 25 | echo -e "${RED}│${NC} ${RED}│${NC}" 26 | echo -e "${RED}│${NC} nlt environment deactivated successfully ${RED}│${NC}" 27 | echo -e "${RED}│${NC} ${RED}│${NC}" 28 | echo -e "${RED}└────────────────────────────────────────────┘${NC}\n" 29 | 30 | # Default values 31 | cleanup=false 32 | additional_param="" 33 | 34 | # Parse command line arguments 35 | while [[ "$#" -gt 0 ]]; do 36 | case $1 in 37 | -c|--cleanup) cleanup=true ;; 38 | *) echo "Unknown parameter: $1. Must be either -c or --cleanup"; exit 1 ;; 39 | esac 40 | shift 41 | done 42 | 43 | 44 | # Check if cleanup flag is set 45 | if [ "$cleanup" = true ] ; then 46 | FILE_DIR=$(dirname "$(dirname "$(readlink -f "$0")")") 47 | 48 | # Print stylized deactivation message 49 | echo -e "\n${RED}┌────────────────────────────────────────────┐${NC}" 50 | echo -e "${RED}│${NC} ${RED}│${NC}" 51 | echo -e "${RED}│${NC} nlt Environment Cleaned Up Successfully ${RED}│${NC}" 52 | echo -e "${RED}│${NC} ${RED}│${NC}" 53 | echo -e "${RED}└────────────────────────────────────────────┘${NC}\n" 54 | 55 | # eval "rm -rf \"$FILE_DIR\" && rmdir \"$FILE_DIR\"" 56 | eval "rm -rf \"$FILE_DIR\"" 57 | fi 58 | 59 | # Unset the function itself 60 | # unset -f nlt_end -------------------------------------------------------------------------------- /exampleEnv/static/os_info.json: -------------------------------------------------------------------------------- 1 | {"OS Information": "OS: Darwin 23.1.0 (Darwin Kernel Version 23.1.0: Mon Oct 9 21:32:11 PDT 2023; root:xnu-10002.41.9~7/RELEASE_ARM64_T6030)\nMacOS Version: 14.1\nArchitecture: arm64\nAppleScript Support: Available", "Machine Information": "Machine: arm64, Processor: arm", "Network Information": "Hostname: Danqings-MBP, IP: 192.168.50.167", "Memory Information": "Total Memory: 36.00 GB, Available: 12.49 GB", "Disk Information": "Total Disk: 460.43 GB, Free: 75.82 GB", "Current Shell": "/bin/zsh", "Python Version": "3.10.15", "CPU Information": "CPU Cores: 12, Threads: 12", "GPU Information": "Chipset Model: Apple M3 Pro", "Specific OS Information": "Xcode: Xcode 15.4"} -------------------------------------------------------------------------------- /execution_permissions.log: -------------------------------------------------------------------------------- 1 | y 2 | ls -a -------------------------------------------------------------------------------- /natural_language_terminal/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PathOnAIOrg/NaturalLanguageTerminal/ba74875b254a38d7ea41a1fb7bec075730701f99/natural_language_terminal/.DS_Store -------------------------------------------------------------------------------- /natural_language_terminal/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PathOnAIOrg/NaturalLanguageTerminal/ba74875b254a38d7ea41a1fb7bec075730701f99/natural_language_terminal/__init__.py -------------------------------------------------------------------------------- /natural_language_terminal/__main__.py: -------------------------------------------------------------------------------- 1 | from .cli.app import app as create_app 2 | from dotenv import load_dotenv 3 | 4 | if __name__ == "__main__": 5 | load_dotenv() 6 | 7 | create_app() -------------------------------------------------------------------------------- /natural_language_terminal/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PathOnAIOrg/NaturalLanguageTerminal/ba74875b254a38d7ea41a1fb7bec075730701f99/natural_language_terminal/cli/__init__.py -------------------------------------------------------------------------------- /natural_language_terminal/cli/app.py: -------------------------------------------------------------------------------- 1 | import typer 2 | import time 3 | import os 4 | import json 5 | from rich.console import Console 6 | from rich.panel import Panel 7 | from rich.progress import Progress, SpinnerColumn, TextColumn 8 | from rich.prompt import Prompt 9 | from ..create_env import create_nlt_environment 10 | 11 | app = typer.Typer() 12 | console = Console() 13 | 14 | CONFIG_FILE = os.path.expanduser("~/.nltconfig.json") 15 | 16 | def check_init(): 17 | return os.path.exists(CONFIG_FILE) 18 | 19 | @app.command() 20 | def init(): 21 | """Initialize nlt configuration interactively.""" 22 | if check_init(): 23 | console.print("[yellow]Config file already exists. Overwriting...[/yellow]") 24 | 25 | config = { 26 | "API_KEYS": { 27 | "OPENAI_API_KEY": None, 28 | # "GROQ_API_KEY": None 29 | }, 30 | "LLM_OPTIONS": ["gpt-4", "gpt-3.5-turbo", "gpt-4-turbo-preview"], 31 | "CURRENT_LLM": None 32 | } 33 | 34 | console.print("[bold]Welcome to nlt Configuration![/bold]") 35 | 36 | for api in config["API_KEYS"]: 37 | value = Prompt.ask(f"Enter your {api} API key", default="") 38 | config["API_KEYS"][api] = value if value else None 39 | 40 | current_llm = Prompt.ask("Choose your default LLM", choices=config["LLM_OPTIONS"], default=config["LLM_OPTIONS"][0]) 41 | config["CURRENT_LLM"] = current_llm 42 | 43 | with open(CONFIG_FILE, 'w') as f: 44 | json.dump(config, f, indent=2) 45 | 46 | console.print(Panel.fit( 47 | "[bold green]nlt configuration initialized![/bold green]\n\n" 48 | f"Config file created at: [cyan]{CONFIG_FILE}[/cyan]\n" 49 | "You can edit this file manually later if needed.", 50 | title="nlt Initialization", 51 | border_style="green", 52 | )) 53 | 54 | @app.command() 55 | def create(env_name: str): 56 | """Create a new nlt environment.""" 57 | if not check_init(): 58 | console.print("[bold red]Error: nlt is not initialized.[/bold red]") 59 | console.print("Please run [cyan]@nlt init[/cyan] first to set up your configuration.") 60 | return 61 | 62 | with Progress( 63 | SpinnerColumn(), 64 | TextColumn("[progress.description]{task.description}"), 65 | transient=True, 66 | ) as progress: 67 | task = progress.add_task(description="Creating nlt environment...", total=None) 68 | status = create_nlt_environment(env_name) 69 | progress.update(task, completed=100) 70 | time.sleep(0.5) # Allow a moment for the spinner to complete visually 71 | # return 72 | 73 | if status == 0: 74 | # console.print(f"[green]nlt environment '{env_name}' created successfully.[/green]") 75 | console.print(f"\n") 76 | elif status == 1: 77 | console.print(f"[bold red]Error:[/bold red] Directory '{env_name}' already exists", style="red") 78 | 79 | 80 | 81 | console.print(Panel.fit( 82 | f"[bold green]nlt environment '{env_name}' created successfully![/bold green]\n\n" 83 | f"To activate, run:\n" 84 | f"[cyan]source {env_name}/bin/activate[/cyan]\n\n" 85 | f"To deactivate, run:\n" 86 | f"[cyan]deactivate[/cyan]\n", 87 | title="nlt Environment Created", 88 | border_style="green", 89 | )) 90 | 91 | @app.command() 92 | def config(): 93 | """View or edit the current configuration.""" 94 | if not check_init(): 95 | console.print("[bold red]Error: nlt is not initialized.[/bold red]") 96 | console.print("Please run [cyan]@nlt init[/cyan] first to set up your configuration.") 97 | return 98 | 99 | with open(CONFIG_FILE, 'r') as f: 100 | config = json.load(f) 101 | 102 | console.print("[bold]Current Configuration:[/bold]") 103 | console.print(json.dumps(config, indent=2)) 104 | 105 | if Prompt.ask("Do you want to edit the configuration?", choices=["y", "n"], default="n") == "y": 106 | for api in config["API_KEYS"]: 107 | value = Prompt.ask(f"Enter your {api} API key", default=config["API_KEYS"][api] or "") 108 | config["API_KEYS"][api] = value if value else None 109 | 110 | current_llm = Prompt.ask("Choose your default LLM", choices=config["LLM_OPTIONS"], default=config["CURRENT_LLM"]) 111 | config["CURRENT_LLM"] = current_llm 112 | 113 | with open(CONFIG_FILE, 'w') as f: 114 | json.dump(config, f, indent=2) 115 | 116 | console.print("[green]Configuration updated successfully.[/green]") 117 | else: 118 | console.print("Configuration not changed.") -------------------------------------------------------------------------------- /natural_language_terminal/core/system.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import socket 4 | import psutil 5 | import subprocess 6 | 7 | class SystemInfo: 8 | def __init__(self): 9 | self.os_info = platform.system() 10 | self.os_release = platform.release() 11 | self.os_version = platform.version() 12 | self.machine = platform.machine() 13 | self.processor = platform.processor() 14 | self.hostname = socket.gethostname() 15 | self.ip_address = socket.gethostbyname(socket.gethostname()) 16 | 17 | def get_os_info(self): 18 | os_details = f"OS: {self.os_info} {self.os_release} ({self.os_version})" 19 | 20 | if self.os_info == "Darwin": # macOS 21 | mac_version = platform.mac_ver() 22 | os_details += f"\nMacOS Version: {mac_version[0]}" 23 | os_details += f"\nArchitecture: {mac_version[2]}" 24 | os_details += "\nAppleScript Support: Available" 25 | elif self.os_info == "Windows": 26 | win_version = platform.win32_ver() 27 | os_details += f"\nWindows Version: {win_version[0]}" 28 | os_details += f"\nService Pack: {win_version[2]}" 29 | os_details += "\nPowerShell Support: Available" 30 | elif self.os_info == "Linux": 31 | try: 32 | distro = subprocess.check_output(["lsb_release", "-ds"]).decode().strip() 33 | os_details += f"\nDistribution: {distro}" 34 | except: 35 | os_details += "\nDistribution: Unable to determine" 36 | os_details += "\nBash Support: Available" 37 | 38 | return os_details 39 | 40 | def get_machine_info(self): 41 | return f"Machine: {self.machine}, Processor: {self.processor}" 42 | 43 | def get_network_info(self): 44 | return f"Hostname: {self.hostname}, IP: {self.ip_address}" 45 | 46 | def get_memory_info(self): 47 | mem = psutil.virtual_memory() 48 | return f"Total Memory: {self.bytes_to_gb(mem.total):.2f} GB, Available: {self.bytes_to_gb(mem.available):.2f} GB" 49 | 50 | def get_disk_info(self): 51 | disk = psutil.disk_usage('/') 52 | return f"Total Disk: {self.bytes_to_gb(disk.total):.2f} GB, Free: {self.bytes_to_gb(disk.free):.2f} GB" 53 | 54 | def get_current_shell(self): 55 | return os.environ.get('SHELL', 'Unknown') 56 | 57 | def get_python_version(self): 58 | return platform.python_version() 59 | 60 | def get_cpu_info(self): 61 | return f"CPU Cores: {psutil.cpu_count(logical=False)}, Threads: {psutil.cpu_count(logical=True)}" 62 | 63 | def get_gpu_info(self): 64 | try: 65 | if self.os_info == 'Windows': 66 | gpu_info = subprocess.check_output(['wmic', 'path', 'win32_VideoController', 'get', 'name']).decode().strip().split('\n')[1:] 67 | elif self.os_info == 'Darwin': # macOS 68 | gpu_info = subprocess.check_output(['system_profiler', 'SPDisplaysDataType']).decode().strip().split('\n') 69 | gpu_info = [line.strip() for line in gpu_info if 'Chipset Model:' in line] 70 | else: # Linux 71 | gpu_info = subprocess.check_output(['lspci', '-v']).decode().strip().split('\n') 72 | gpu_info = [line for line in gpu_info if 'VGA' in line or '3D' in line] 73 | return "\n".join(gpu_info).strip() 74 | except: 75 | return "GPU information not available" 76 | 77 | def get_specific_os_info(self): 78 | if self.os_info == "Darwin": # macOS 79 | try: 80 | xcode_version = subprocess.check_output(["xcodebuild", "-version"]).decode().strip().split("\n")[0] 81 | return f"Xcode: {xcode_version}" 82 | except: 83 | return "Xcode: Not installed or unable to determine version" 84 | elif self.os_info == "Windows": 85 | try: 86 | powershell_version = subprocess.check_output(["powershell", "$PSVersionTable.PSVersion"]).decode().strip() 87 | return f"PowerShell Version: {powershell_version}" 88 | except: 89 | return "PowerShell: Unable to determine version" 90 | elif self.os_info == "Linux": 91 | try: 92 | kernel_version = subprocess.check_output(["uname", "-r"]).decode().strip() 93 | return f"Kernel Version: {kernel_version}" 94 | except: 95 | return "Kernel: Unable to determine version" 96 | 97 | @staticmethod 98 | def bytes_to_gb(bytes_value): 99 | return bytes_value / (1024 ** 3) 100 | 101 | def get_all_info(self): 102 | return { 103 | "OS Information": self.get_os_info(), 104 | "Machine Information": self.get_machine_info(), 105 | "Network Information": self.get_network_info(), 106 | "Memory Information": self.get_memory_info(), 107 | "Disk Information": self.get_disk_info(), 108 | "Current Shell": self.get_current_shell(), 109 | "Python Version": self.get_python_version(), 110 | "CPU Information": self.get_cpu_info(), 111 | "GPU Information": self.get_gpu_info(), 112 | "Specific OS Information": self.get_specific_os_info() 113 | } 114 | 115 | if __name__ == "__main__": 116 | sys_info = SystemInfo() 117 | all_info = sys_info.get_all_info() 118 | 119 | for key, value in all_info.items(): 120 | print(f"{key}:") 121 | print(f" {value}") 122 | print() -------------------------------------------------------------------------------- /natural_language_terminal/create_env.py: -------------------------------------------------------------------------------- 1 | import os 2 | import typer 3 | import json 4 | from rich.console import Console 5 | from natural_language_terminal.core.system import SystemInfo 6 | 7 | console = Console() 8 | 9 | def create_nlt_environment(env_name): 10 | if os.path.exists(env_name): 11 | return 1 12 | 13 | os.makedirs(os.path.join(env_name, 'bin')) 14 | os.makedirs(os.path.join(env_name, 'static')) 15 | 16 | script_dir = os.path.dirname(os.path.abspath(__file__)) 17 | 18 | # MacOS-specific scripts 19 | scripts = [ 20 | ('activate_template.sh', 'activate'), 21 | ('end_template.sh', 'nlt_end'), 22 | ('interceptor.sh', 'interceptor.sh'), 23 | ('env_setter.sh', 'env_setter.sh') 24 | ] 25 | 26 | for template, new_name in scripts: 27 | template_path = os.path.join(script_dir, 'shell_scripts', template) 28 | output_path = os.path.join(env_name, 'bin', new_name) 29 | 30 | # Read with Unix line endings 31 | with open(template_path, 'r', encoding='utf-8', newline='\n') as f: 32 | content = f.read() 33 | 34 | # Replace environment name 35 | content = content.replace('{{ENV_NAME}}', env_name) 36 | 37 | # Write with Unix line endings 38 | with open(output_path, 'w', encoding='utf-8', newline='\n') as f: 39 | f.write(content) 40 | 41 | # Make scripts executable 42 | os.chmod(output_path, 0o755) 43 | 44 | # Write system info 45 | system_info = SystemInfo() 46 | with open(os.path.join(env_name, 'static', 'os_info.json'), 'w', encoding='utf-8') as f: 47 | f.write(json.dumps(system_info.get_all_info())) 48 | 49 | return 0 50 | 51 | if __name__ == "__main__": 52 | typer.run(create_nlt_environment) -------------------------------------------------------------------------------- /natural_language_terminal/git/autocommit.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | from openai import OpenAI 4 | import os 5 | from rich.console import Console 6 | from rich.panel import Panel 7 | from rich.progress import Progress, SpinnerColumn, TextColumn 8 | 9 | from natural_language_terminal.prompts import AUTOCOMMIT_ASSISTANT_PROMPT, AUTOCOMMIT_SYSTEM_PROMPT 10 | 11 | console = Console() 12 | client = OpenAI() 13 | 14 | def get_git_diff(): 15 | try: 16 | return subprocess.check_output(['git', 'diff', '--staged']).decode('utf-8') 17 | except subprocess.CalledProcessError: 18 | console.print("[bold red]Error:[/bold red] Not a git repository or no changes staged.", style="red") 19 | sys.exit(1) 20 | 21 | def generate_commit_message(diff): 22 | # openai.api_key = os.getenv('OPENAI_API_KEY') 23 | 24 | if not os.getenv('OPENAI_API_KEY'): 25 | console.print("[bold red]Error:[/bold red] OPENAI_API_KEY environment variable not set.", style="red") 26 | sys.exit(1) 27 | 28 | with Progress( 29 | SpinnerColumn(), 30 | TextColumn("[progress.description]{task.description}"), 31 | transient=True, 32 | ) as progress: 33 | progress.add_task(description="Generating commit message...", total=None) 34 | try: 35 | response = client.chat.completions.create( 36 | model="gpt-4o", 37 | messages=[ 38 | {"role": "system", "content": AUTOCOMMIT_SYSTEM_PROMPT}, 39 | {"role": "user", "content": AUTOCOMMIT_ASSISTANT_PROMPT % diff} 40 | ] 41 | ) 42 | return response.choices[0].message.content.strip() 43 | except Exception as e: 44 | console.print(f"[bold red]Error generating commit message:[/bold red] {str(e)}", style="red") 45 | sys.exit(1) 46 | 47 | def create_commit(message): 48 | try: 49 | subprocess.run(['git', 'commit', '-m', message], check=True) 50 | console.print(Panel.fit( 51 | f"[bold green]Commit created successfully![/bold green]\n\n" 52 | f"Message: [cyan]{message}[/cyan]", 53 | title="Git AutoCommit", 54 | border_style="green", 55 | )) 56 | except subprocess.CalledProcessError: 57 | console.print("[bold red]Error:[/bold red] Failed to create commit.", style="red") 58 | sys.exit(1) 59 | 60 | def main(): 61 | diff = get_git_diff() 62 | if not diff: 63 | console.print("No changes to commit.", style="yellow") 64 | return 65 | 66 | commit_message = generate_commit_message(diff) 67 | create_commit(commit_message) 68 | 69 | if __name__ == "__main__": 70 | main() -------------------------------------------------------------------------------- /natural_language_terminal/prompts.py: -------------------------------------------------------------------------------- 1 | AUTOCOMMIT_SYSTEM_PROMPT = "You are a helpful and skilled coding assistant that generates concise and informative git commit messages based on the provided git diff." 2 | 3 | AUTOCOMMIT_ASSISTANT_PROMPT = """ 4 | Generate a commit message for the following git diff:\n\n %s 5 | 6 | It is strongly recommended your git commit follows the format below (see table for examples) 7 | 8 | git commit -m : 9 | 10 | | | | 11 | |-------------|--------------------------------------------------| 12 | | `feat` | Add new features | 13 | | `fix` | Fix bugs | 14 | | `docs` | Modify documents like README, CONTRIBUTE | 15 | | `style` | Modify code format like space and comma without changing code logic | 16 | | `refactor` | Refactor code structure without adding new features or fixing new bugs | 17 | | `perf` | Improve performance or user experience | 18 | | `test` | Test features, including unit test and integration test | 19 | | `chore` | Change the build procedure or add dependencies | 20 | | `revert` | Revert to the previous version | 21 | 22 | Make sure to only output the : , no extra formatting or anything like ```. JUST THE CONTENT. THERE SHOULD BE NO MARKDOWN. some sample responses are below 23 | 24 | chore: agent reqs check 25 | test: rewrite test for refactored code 26 | refactor: add react agent as abstraction of existing example agents 27 | 28 | IT SHOULDNT BE MORE THAN A SINGLE LINE AAAH 29 | """ 30 | 31 | 32 | TERMINAL_COMMAND_SYSTEM_PROMPT = None 33 | 34 | TERMINAL_COMMAND_ASSISTANT_PROMPT = """ 35 | You are an AI assistant skilled in terminal and command line operations. You will be given a phrase, and it's your job to provide the most appropriate zsh shell command(s) for macOS to accomplish the task. 36 | 37 | Your response should contain ONLY the command(s), with no additional explanation or formatting. Follow these guidelines: 38 | 39 | 1. For single commands, provide them on one line. 40 | 2. For multi-step operations, put each command on a new line. 41 | 3. If a command is too long, you may split it across multiple lines using backslashes (\\) for line continuation. 42 | 4. Do not use any markdown formatting. 43 | 5. Do not include any explanatory text or comments. 44 | 45 | Examples of valid responses: 46 | 47 | Single command: 48 | ls -la 49 | 50 | Multi-step operation: 51 | python3 -m venv myenv 52 | source myenv/bin/activate 53 | pip install requests 54 | 55 | Long command split across lines: 56 | ffmpeg -i input.mp4 \\ 57 | -c:v libx264 -preset slow -crf 22 \\ 58 | -c:a copy \\ 59 | output.mp4 60 | 61 | The phrase is: %s 62 | 63 | Provide the appropriate command(s) now: 64 | """ 65 | -------------------------------------------------------------------------------- /natural_language_terminal/shell_scripts/activate_template.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | 4 | if "%1"=="" goto :no_args 5 | if "%1"=="disable" goto :disable_alias 6 | goto :invalid_arg 7 | 8 | :no_args 9 | where python >nul 2>&1 10 | if %errorlevel% equ 0 ( 11 | set "nlt_PYTHON_PATH=python" 12 | goto :setup_alias 13 | ) 14 | 15 | where python3 >nul 2>&1 16 | if %errorlevel% equ 0 ( 17 | set "nlt_PYTHON_PATH=python3" 18 | goto :setup_alias 19 | ) 20 | 21 | exit /b 1 22 | 23 | :setup_alias 24 | set "TEMP_FILE=%TEMP%\git_alias_command.txt" 25 | 26 | for %%I in ("%~dp0..\..") do set "PARENT_FOLDER=%%~nxI" 27 | 28 | git config --global alias.autocommit "^!%nlt_PYTHON_PATH% -c \"from nlt.git.autocommit import main; main()\"" 29 | 30 | set GIT_AUTOCOMMIT_ACTIVE=1 31 | 32 | :: Define the escape character 33 | for /F %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a" 34 | 35 | :: Set the colored prefix 36 | set "NAVY_BLUE_PREFIX=%ESC%[94m(nlt~%PARENT_FOLDER%)%ESC%[0m" 37 | 38 | :: Modify the prompt 39 | prompt %NAVY_BLUE_PREFIX% $P$G 40 | 41 | :: Start a new command prompt session 42 | cmd /k 43 | 44 | del "%TEMP_FILE%" 45 | exit /b 0 46 | 47 | :disable_alias 48 | git config --global --unset alias.autocommit 49 | set GIT_AUTOCOMMIT_ACTIVE=0 50 | prompt $P$G 51 | cmd /k 52 | exit /b 0 53 | 54 | :invalid_arg 55 | exit /b 1 -------------------------------------------------------------------------------- /natural_language_terminal/shell_scripts/activate_template.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the directory of the current script 4 | FILE_DIR=$(dirname "$(readlink -f "$0")") 5 | 6 | echo "FILE_DIR is set to: $FILE_DIR" 7 | 8 | # Save the current PATH and PS1 9 | export OLD_PATH="$PATH" 10 | export OLD_PS1="$PS1" 11 | 12 | # Add the script directory to PATH 13 | export PATH="$FILE_DIR:$PATH" 14 | 15 | # Save the current Python interpreter path 16 | export nlt_PYTHON_PATH="/opt/homebrew/bin/python3.10" 17 | 18 | # Confirm Python path 19 | echo "nlt_PYTHON_PATH is set to: $nlt_PYTHON_PATH" 20 | 21 | # Create the git-autocommit alias using the full path to Python 22 | git config --global alias.autocommit "!$nlt_PYTHON_PATH -c \"from natural_language_terminal.git.autocommit import main; main()\"" 23 | 24 | alias remove="source $FILE_DIR/nlt_end" 25 | 26 | source "$FILE_DIR/interceptor.sh" 27 | 28 | 29 | # Before eval "intercept" 30 | echo "About to run intercept function" 31 | 32 | intercept 33 | echo "Intercept function executed" 34 | 35 | # Set a flag to indicate nlt is active 36 | export nlt_ACTIVE=1 37 | 38 | source "$FILE_DIR/env_setter.sh" 39 | 40 | if [ -n "$BASH_VERSION" ]; then 41 | # Navy blue color code for Bash 42 | navy_blue="\[\e[38;5;17m\]" 43 | reset_color="\[\e[0m\]" 44 | 45 | # Modify PS1 for Bash 46 | export PS1="${navy_blue}[nlt~{{ENV_NAME}}]${reset_color} $PS1" 47 | elif [ -n "$ZSH_VERSION" ]; then 48 | # Navy blue color code for Zsh 49 | navy_blue="%F{17}" 50 | reset_color="%f" 51 | 52 | # Modify PROMPT for Zsh 53 | export PROMPT="${navy_blue}[nlt~{{ENV_NAME}}]${reset_color} $PROMPT" 54 | fi 55 | 56 | # ANSI color codes 57 | GREEN='\033[0;32m' 58 | CYAN='\033[0;36m' 59 | YELLOW='\033[1;33m' 60 | NC='\033[0m' # No Color 61 | 62 | # Print stylized activation message 63 | echo -e "\n${GREEN}┌────────────────────────────────────────────┐${NC}" 64 | echo -e "${GREEN}│ │${NC}" 65 | echo -e "${GREEN}│ ${YELLOW}nlt environment activated successfully${NC} ${GREEN}│${NC}" 66 | echo -e "${GREEN}│ │${NC}" 67 | echo -e "${GREEN}├────────────────────────────────────────────┤${NC}" 68 | echo -e "${GREEN}│ │${NC}" 69 | echo -e "${GREEN}│${NC} Environment: ${CYAN}{{ENV_NAME}}${NC} ${GREEN}│${NC}" 70 | echo -e "${GREEN}│ ${NC}To end the session, type: ${CYAN}remove${NC} ${GREEN}│${NC}" 71 | echo -e "${GREEN}│ │${NC}" 72 | echo -e "${GREEN}└────────────────────────────────────────────┘${NC}\n" 73 | -------------------------------------------------------------------------------- /natural_language_terminal/shell_scripts/end_template.bat: -------------------------------------------------------------------------------- 1 | call setup_git_alias.bat disable -------------------------------------------------------------------------------- /natural_language_terminal/shell_scripts/end_template.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Unset the git alias 4 | git config --global --unset alias.autocommit 5 | 6 | # Restore the old PATH and PS1 7 | export PATH="$OLD_PATH" 8 | export PS1="$OLD_PS1" 9 | 10 | #end interceptor 11 | eval "intercept" 12 | 13 | # Unset nlt-specific environment variables 14 | unset nlt_ACTIVE 15 | unset nlt_PYTHON_PATH 16 | unset OLD_PATH 17 | unset OLD_PS1 18 | 19 | # ANSI color codes 20 | RED='\033[0;31m' 21 | NC='\033[0m' # No Color 22 | 23 | # Print stylized deactivation message 24 | echo -e "\n${RED}┌────────────────────────────────────────────┐${NC}" 25 | echo -e "${RED}│${NC} ${RED}│${NC}" 26 | echo -e "${RED}│${NC} nlt environment deactivated successfully ${RED}│${NC}" 27 | echo -e "${RED}│${NC} ${RED}│${NC}" 28 | echo -e "${RED}└────────────────────────────────────────────┘${NC}\n" 29 | 30 | # Default values 31 | cleanup=false 32 | additional_param="" 33 | 34 | # Parse command line arguments 35 | while [[ "$#" -gt 0 ]]; do 36 | case $1 in 37 | -c|--cleanup) cleanup=true ;; 38 | *) echo "Unknown parameter: $1. Must be either -c or --cleanup"; exit 1 ;; 39 | esac 40 | shift 41 | done 42 | 43 | 44 | # Check if cleanup flag is set 45 | if [ "$cleanup" = true ] ; then 46 | FILE_DIR=$(dirname "$(dirname "$(readlink -f "$0")")") 47 | 48 | # Print stylized deactivation message 49 | echo -e "\n${RED}┌────────────────────────────────────────────┐${NC}" 50 | echo -e "${RED}│${NC} ${RED}│${NC}" 51 | echo -e "${RED}│${NC} nlt Environment Cleaned Up Successfully ${RED}│${NC}" 52 | echo -e "${RED}│${NC} ${RED}│${NC}" 53 | echo -e "${RED}└────────────────────────────────────────────┘${NC}\n" 54 | 55 | # eval "rm -rf \"$FILE_DIR\" && rmdir \"$FILE_DIR\"" 56 | eval "rm -rf \"$FILE_DIR\"" 57 | fi 58 | 59 | # Unset the function itself 60 | # unset -f nlt_end -------------------------------------------------------------------------------- /natural_language_terminal/shell_scripts/env_setter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Path to the config file 4 | CONFIG_FILE="$HOME/.nltconfig.json" 5 | 6 | # Check if the config file exists 7 | if [ ! -f "$CONFIG_FILE" ]; then 8 | echo "Error: Config file not found at $CONFIG_FILE" 9 | exit 1 10 | fi 11 | 12 | # Check if jq is installed 13 | if ! command -v jq &> /dev/null; then 14 | echo "Error: jq is not installed. Please install it first." 15 | exit 1 16 | fi 17 | 18 | # Read API keys from the config file and export them 19 | jq -r '.API_KEYS | to_entries[] | "\(.key)=\(.value)"' "$CONFIG_FILE" | while IFS= read -r line; do 20 | # Skip empty lines 21 | [ -z "$line" ] && continue 22 | 23 | # Split the line into key and value 24 | key="${line%%=*}" 25 | value="${line#*=}" 26 | 27 | # Skip null values and only process API_KEY entries 28 | if [ "$value" != "null" ] && [[ $key == *"API_KEY"* ]]; then 29 | # Remove any surrounding quotes from the value 30 | value="${value%\"}" 31 | value="${value#\"}" 32 | 33 | # Export the environment variable 34 | export "$key=$value" 35 | echo "Exported: $key" 36 | fi 37 | done 38 | 39 | -------------------------------------------------------------------------------- /natural_language_terminal/shell_scripts/interceptor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script works for both Bash and Zsh 3 | 4 | # Variable to store intercepted commands 5 | INTERCEPTED_COMMAND="" 6 | 7 | # Variable to control interception state 8 | INTERCEPTION_ACTIVE=false 9 | 10 | # Function to log the command 11 | log_command() { 12 | local cmd="$1" 13 | echo "Command intercepted: $cmd" >> ~/.intercepted_commands.log 14 | } 15 | 16 | # Function to be used as a trap 17 | intercept_command() { 18 | if $INTERCEPTION_ACTIVE; then 19 | # Get the command that's about to be executed 20 | local cmd="$BASH_COMMAND" 21 | 22 | if [[ -n "$ZSH_VERSION" ]]; then 23 | cmd="$1" 24 | # trap - DEBUG 25 | fi 26 | 27 | # Ignore specific commands 28 | case "$cmd" in 29 | intercept|last_intercepted|toggle_interception|show_last_intercepted) 30 | return 0 31 | ;; 32 | esac 33 | 34 | # Store and log the command 35 | INTERCEPTED_COMMAND="$cmd" 36 | log_command "$INTERCEPTED_COMMAND" 37 | 38 | # Prevent the command from executing 39 | if [[ -n "$ZSH_VERSION" ]]; then 40 | return 1 41 | else 42 | kill -INT $$ 43 | fi 44 | fi 45 | } 46 | 47 | # Function to toggle command interception 48 | toggle_interception() { 49 | if $INTERCEPTION_ACTIVE; then 50 | INTERCEPTION_ACTIVE=false 51 | if [[ -n "$ZSH_VERSION" ]]; then 52 | if (( ${#preexec_functions[@]} )); then 53 | preexec_functions=("${preexec_functions[@]:#intercept_command}") 54 | fi 55 | else 56 | trap - DEBUG 57 | fi 58 | else 59 | INTERCEPTION_ACTIVE=true 60 | if [[ -n "$ZSH_VERSION" ]]; then 61 | if ! (( ${preexec_functions[(Ie)intercept_command]} )); then 62 | preexec_functions+=(intercept_command) 63 | fi 64 | else 65 | trap 'intercept_command' DEBUG 66 | fi 67 | fi 68 | } 69 | 70 | # Alias to easily toggle interception 71 | alias intercept='toggle_interception' 72 | 73 | # Function to show the last intercepted command 74 | show_last_intercepted() { 75 | if [[ -n "$INTERCEPTED_COMMAND" ]]; then 76 | echo "Last intercepted command: $INTERCEPTED_COMMAND" 77 | else 78 | echo "No command has been intercepted yet." 79 | fi 80 | } 81 | 82 | # Alias to show last intercepted command 83 | alias last_intercepted='show_last_intercepted' 84 | 85 | # zsh specific handler 86 | command_not_found_handler() { 87 | # Add a guard against recursion 88 | if [[ -n "$IN_COMMAND_HANDLER" ]]; then 89 | echo "zsh: command not found: $*" 90 | return 1 91 | fi 92 | 93 | export IN_COMMAND_HANDLER=1 94 | 95 | if $INTERCEPTION_ACTIVE; then 96 | ai_message_generator "$*" 97 | local result=$? 98 | unset IN_COMMAND_HANDLER 99 | return $result 100 | else 101 | echo "zsh: command not found: $*" 102 | unset IN_COMMAND_HANDLER 103 | return 1 104 | fi 105 | } 106 | 107 | ai_message_generator() { 108 | local cmd="$*" 109 | 110 | eval "$nlt_PYTHON_PATH -c \"from natural_language_terminal.terminal.base import main; main('$cmd')\"" 111 | 112 | if [ $? -ne 0 ]; then 113 | echo "Error occurred while processing the API response." 114 | return 1 115 | fi 116 | 117 | filename="execution_permissions.log" 118 | 119 | # Check if the file exists 120 | if [ ! -f "$filename" ]; then 121 | echo "Error: File '$filename' not found" 122 | exit 1 123 | fi 124 | 125 | # Read the first two lines from the file 126 | read -r line1 < "$filename" 127 | read -r line2 < <(sed -n '2p' "$filename") 128 | 129 | # Convert line1 to lowercase for case-insensitive comparison 130 | line1_lower=$(echo "$line1" | tr '[:upper:]' '[:lower:]') 131 | 132 | # Check if the first line is 'y' 133 | if [ "$line1_lower" = "y" ]; then 134 | eval "$line2" 135 | else 136 | echo "No command executed" 137 | fi 138 | 139 | return 0 140 | } 141 | -------------------------------------------------------------------------------- /natural_language_terminal/terminal/base.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import json 3 | import os 4 | import requests 5 | 6 | from rich.console import Console 7 | from rich.panel import Panel 8 | from rich.syntax import Syntax 9 | from rich.text import Text 10 | 11 | from natural_language_terminal.prompts import TERMINAL_COMMAND_ASSISTANT_PROMPT 12 | from natural_language_terminal.terminal.utils import parse_ai_response_for_bash_and_zsh 13 | 14 | from dotenv import load_dotenv 15 | 16 | load_dotenv() 17 | 18 | console = Console() 19 | 20 | def format_response(content: str) -> Panel: 21 | title = Text("nlt Suggestion", style="bold magenta") 22 | syntax = Syntax(content, "markdown", theme="monokai", line_numbers=True) 23 | return Panel(syntax, title=title, expand=False, border_style="green") 24 | 25 | def make_prompt(cmd: str): 26 | print(cmd) 27 | return TERMINAL_COMMAND_ASSISTANT_PROMPT % cmd 28 | 29 | def make_api_request(cmd: str, api_url: str, api_key: str) -> dict: 30 | prompt = make_prompt(cmd) 31 | 32 | headers = { 33 | "Content-Type": "application/json", 34 | "Authorization": f"Bearer {api_key}" 35 | } 36 | 37 | data = { 38 | "model": "gpt-4o", 39 | "messages": [{"role": "user", "content": prompt}], 40 | "temperature": 0.7 41 | } 42 | 43 | with console.status("Thinking...", spinner="dots"): 44 | response = requests.post(api_url, headers=headers, json=data) 45 | response.raise_for_status() 46 | return response.json() 47 | 48 | def write_to_file(user_choice: str, generated_command: str): 49 | with open("execution_permissions.log", "w") as f: 50 | f.write(f"{user_choice}\n{parse_ai_response_for_bash_and_zsh(generated_command)}") 51 | 52 | def main(command: str): 53 | api_url = 'https://api.openai.com/v1/chat/completions' 54 | api_key = os.getenv('OPENAI_API_KEY') 55 | 56 | if not api_url or not api_key: 57 | console.print("[bold red]Error:[/bold red] API_URL and API_KEY must be set as environment variables") 58 | sys.exit(1) 59 | 60 | try: 61 | response = make_api_request(command, api_url, api_key) 62 | content = response['choices'][0]['message']['content'] 63 | 64 | formatted_response = format_response(content) 65 | console.print(formatted_response) 66 | 67 | user_choice = console.input("Do you want to run this command? (y/n): ").lower() 68 | write_to_file(user_choice, content) 69 | 70 | except requests.RequestException as e: 71 | console.print(f"[bold red]Error making API request:[/bold red] {str(e)}") 72 | sys.exit(1) 73 | except (KeyError, IndexError) as e: 74 | console.print(f"[bold red]Error parsing API response:[/bold red] {str(e)}") 75 | console.print(Panel(json.dumps(response, indent=2), title="Raw Response", border_style="red")) 76 | sys.exit(1) 77 | except Exception as e: 78 | console.print(f"[bold red]An unexpected error occurred:[/bold red] {str(e)}") 79 | sys.exit(1) 80 | 81 | if __name__ == "__main__": 82 | main() -------------------------------------------------------------------------------- /natural_language_terminal/terminal/utils.py: -------------------------------------------------------------------------------- 1 | def parse_ai_response_for_bash_and_zsh(ai_response: str): 2 | # Remove any leading/trailing whitespace 3 | ai_response = ai_response.strip() 4 | 5 | # Split the response into lines 6 | lines = ai_response.split('\n') 7 | 8 | parsed_commands = [] 9 | current_command = "" 10 | 11 | for line in lines: 12 | line = line.strip() 13 | if not line: 14 | continue 15 | 16 | # Check if the line ends with a backslash (line continuation) 17 | if line.endswith('\\'): 18 | current_command += line[:-1] + " " 19 | else: 20 | current_command += line 21 | parsed_commands.append(current_command.strip()) 22 | current_command = "" 23 | 24 | # If there's any remaining command (in case the last line had a backslash) 25 | if current_command: 26 | parsed_commands.append(current_command.strip()) 27 | 28 | # Join commands with semicolons for bash execution 29 | bash_ready_command = '; '.join(parsed_commands) 30 | 31 | # Escape single quotes 32 | bash_ready_command = bash_ready_command.replace("'", "'\\''") 33 | 34 | return bash_ready_command 35 | -------------------------------------------------------------------------------- /nlt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PathOnAIOrg/NaturalLanguageTerminal/ba74875b254a38d7ea41a1fb7bec075730701f99/nlt.png -------------------------------------------------------------------------------- /nlt.py: -------------------------------------------------------------------------------- 1 | from natural_language_terminal import * -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | openai 2 | typer 3 | rich 4 | python-dotenv 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import codecs 3 | import os 4 | 5 | here = os.path.abspath(os.path.dirname(__file__)) 6 | 7 | with codecs.open(os.path.join(here, "README.md"), encoding="utf-8") as fh: 8 | long_description = "\n" + fh.read() 9 | 10 | setup( 11 | name='natural_language_terminal', 12 | version='0.1.9', # Increment the version number 13 | packages=[ 14 | 'natural_language_terminal', 15 | 'natural_language_terminal.cli', 16 | 'natural_language_terminal.core', 17 | 'natural_language_terminal.git', 18 | 'natural_language_terminal.terminal', 19 | ], 20 | package_data={ 21 | 'natural_language_terminal': [ 22 | 'shell_scripts/*.sh', 23 | 'shell_scripts/*.bat' 24 | ], 25 | }, 26 | long_description=long_description, 27 | long_description_content_type='text/markdown', 28 | include_package_data=True, 29 | author='Balaji Rama', 30 | author_email='balajirw10@gmail.com', 31 | python_requires='>=3.6', 32 | install_requires=['openai', 'typer', 'rich', 'python-dotenv'], 33 | entry_points={ 34 | "console_scripts": [ 35 | "nlt=natural_language_terminal.cli.app:app", 36 | ], 37 | }, 38 | ) --------------------------------------------------------------------------------