├── LICENSE ├── README.md └── hubcap.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Dave Hulbert 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hubcap 2 | Hubcap is an autonomous AI agent in 25 lines of code: a small Autobot that you can't trust 3 | 4 | ## What is it? 5 | 6 | Hubcap is a commandline tool. It takes a goal from the user, then uses OpenAI's GPT to work out how to complete it. 7 | It keeps going autonomously until the goal is completed. 8 | 9 | Hubcap is desigend to be as minimal as possible yet get reasonable results. It works but it's little more than a demo. 10 | There's lots that could be done to improve it but a better use of time would be using a more comprehensive 11 | autonomous agent. 12 | 13 | As the code is so simple, it serves as a quick intro to how autonomous agents work and how to interact with LLMs. 14 | 15 | Check out my article **[Amazingly Alarming Autonomous AI Agents 16 | ](https://medium.com/@dave1010/amazingly-alarming-autonomous-ai-agents-62f8a785e4d8)** for an intro to AI agents and details about how Hubcap works. 17 | 18 | Hubcap has also been [ported to Python](https://gist.github.com/jefftriplett/d5b1c10f0fe2a2e2afa2a6658143c896) by @jefftriplett. 19 | 20 | ## Installation 21 | 22 | ```shell 23 | pip install llm 24 | llm keys set openai 25 | 26 | # install our other Python libraries 27 | pip install rich typer 28 | ``` 29 | 30 | ## Usage 31 | 32 | ```shell 33 | python hubcap.py 34 | ``` 35 | 36 | ## Examples 37 | 38 | Fix bugs 39 | 40 | ```shell 41 | python hubcap.py "factorial.py is broken. take a look and fix it for me" 42 | ``` 43 | 44 | Process files 45 | 46 | ```shell 47 | python hubcap.py "use ffmpeg to downscale all the videos in the current dir" 48 | ``` 49 | 50 | Do all your work for you 51 | 52 | ```shell 53 | # requires GPT-5 54 | 55 | python hubcap.py "use the jira cli to fetch all my tasks. work through them in priority order until they're all done." 56 | ``` 57 | 58 | fixing python script 59 | 60 | 61 | 62 | ## Security 63 | 64 | **Hubcap will run whatever commands that GPT gives it! You must keep your eye on what it's doing at all times.** 65 | 66 | Fortunately, it shows the command, then waits 3 seconds before executing it. So you can easily stop it if you notice it doing something it shouldn't. 67 | 68 | It may get stuck in a loop and use up your OpenAI credit. Set a budget! 69 | 70 | ## Why "Hubcap"? 71 | 72 | > _Everybody likes Hubcap. He's friendly, always has a clever joke or compliment ready, and is generally a nice, fun, affable bot to be around. On the other hand, nobody actually trusts Hubcap. It's not his devotion to the Autobot cause that's the concern (mostly), but his ulterior motives for his friendship and jokes and the like. He is at his core a con artist, and everyone's a bit on their guard around him. Some think he's this way to cover for deficiencies or to stay in a position well above his actual aptitude level... or maybe he just likes tricking people. He doesn't seem to have made many enemies with his tricks and scams, though; quite the opposite. So perhaps it is all mostly harmless._ 73 | > https://tfwiki.net/wiki/Hubcap_(G1) 74 | > 75 | > ![Hubcap](https://github.com/dave1010/hubcap/assets/50682/ce83ca64-1236-45b8-9725-523a864b030a) 76 | 77 | ## Licence 78 | 79 | MIT Licence 80 | 81 | (c) 2023 Dave Hulbert 82 | -------------------------------------------------------------------------------- /hubcap.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import time 3 | import typer 4 | 5 | from rich import print 6 | from shlex import quote 7 | 8 | SYSTEM = ( 9 | "you are a coding agent. " 10 | """you can read and write files. Eg `cat helloworld.txt`, `echo "hello\nworld" > helloworld.txt` output the next command required to progress your goal. """ 11 | "output `DONE` when done." 12 | ) 13 | 14 | 15 | def chat(*, prompt: str, system: str | None = None) -> str: 16 | if system: 17 | print(f"[green][PROMPT][/green] {system}") 18 | options = f"--system {quote(system)}" 19 | else: 20 | options = "--continue" 21 | 22 | print(f"[blue][PROMPT][/blue] {prompt}") 23 | response = subprocess.getoutput(f"llm {options} {quote(prompt)}\n") 24 | 25 | print(f"[yellow][RESPONSE][/yellow] {response}") 26 | return response 27 | 28 | 29 | def main(prompt: str, sleep: int = 3): 30 | response = chat( 31 | prompt=f"GOAL: {prompt}\n\nWHAT IS YOUR OVERALL PLAN?", system=SYSTEM 32 | ) 33 | 34 | while True: 35 | response = chat( 36 | prompt="SHELL COMMAND TO EXECUTE OR `DONE`. NO ADDITIONAL CONTEXT OR EXPLANATION:" 37 | ).strip() 38 | if response == "DONE": 39 | break 40 | 41 | time.sleep(sleep) 42 | 43 | try: 44 | output = subprocess.check_output( 45 | response, stderr=subprocess.STDOUT, shell=True 46 | ).decode() 47 | return_code = 0 48 | except subprocess.CalledProcessError as e: 49 | output = e.output.decode() 50 | return_code = e.returncode 51 | 52 | response = chat( 53 | prompt=f"COMMAND COMPLETED WITH RETURN CODE: {return_code}. OUTPUT:\n" 54 | f"{output}\n\n" 55 | "WHAT ARE YOUR OBSERVATIONS?" 56 | ) 57 | 58 | 59 | if __name__ == "__main__": 60 | typer.run(main) 61 | --------------------------------------------------------------------------------