penguin-agent-package 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. penguin_agent_package-0.1.0/.github/workflows/python-app.yml +39 -0
  2. penguin_agent_package-0.1.0/.gitignore +4 -0
  3. penguin_agent_package-0.1.0/.idea/.gitignore +5 -0
  4. penguin_agent_package-0.1.0/.idea/.name +1 -0
  5. penguin_agent_package-0.1.0/.idea/inspectionProfiles/profiles_settings.xml +6 -0
  6. penguin_agent_package-0.1.0/.idea/micropython-tools-settings.xml +12 -0
  7. penguin_agent_package-0.1.0/.idea/misc.xml +4 -0
  8. penguin_agent_package-0.1.0/.idea/modules.xml +9 -0
  9. penguin_agent_package-0.1.0/.idea/penguin-agent-package.iml +11 -0
  10. penguin_agent_package-0.1.0/.idea/penguin-agent.iml +11 -0
  11. penguin_agent_package-0.1.0/.idea/shelf/Uncommitted_changes_before_Update_at_09_10_26,_21_02_[Changes]/shelved.patch +207 -0
  12. penguin_agent_package-0.1.0/.idea/shelf/Uncommitted_changes_before_Update_at_09_10_26__21_02__Changes_.xml +4 -0
  13. penguin_agent_package-0.1.0/.idea/vcs.xml +6 -0
  14. penguin_agent_package-0.1.0/.idea/workspace.xml +164 -0
  15. penguin_agent_package-0.1.0/LICENSE +21 -0
  16. penguin_agent_package-0.1.0/PKG-INFO +82 -0
  17. penguin_agent_package-0.1.0/README.md +51 -0
  18. penguin_agent_package-0.1.0/pyproject.toml +40 -0
  19. penguin_agent_package-0.1.0/requirements.txt +20 -0
  20. penguin_agent_package-0.1.0/ressources/bildschirmaufzeichnung-vom-2026-09-26-23-34-26_q3iOhA72 (online-video-cutter.com).gif +0 -0
  21. penguin_agent_package-0.1.0/ressources/bildschirmaufzeichnung-vom-2026-09-26-23-34-26_q3iOhA72 (online-video-cutter.com).mp4 +0 -0
  22. penguin_agent_package-0.1.0/src/penguin-agent/__main__.py +159 -0
@@ -0,0 +1,39 @@
1
+ # This workflow will install Python dependencies, run tests and lint with a single version of Python
2
+ # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3
+
4
+ name: Python application
5
+
6
+ on:
7
+ push:
8
+ branches: [ "main" ]
9
+ pull_request:
10
+ branches: [ "main" ]
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ build:
17
+
18
+ runs-on: ubuntu-latest
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - name: Set up Python 3.10
23
+ uses: actions/setup-python@v3
24
+ with:
25
+ python-version: "3.10"
26
+ - name: Install dependencies
27
+ run: |
28
+ python -m pip install --upgrade pip
29
+ pip install flake8 pytest
30
+ if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
31
+ - name: Lint with flake8
32
+ run: |
33
+ # stop the build if there are Python syntax errors or undefined names
34
+ flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
35
+ # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
36
+ flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
37
+ - name: Test with pytest
38
+ run: |
39
+ pytest
@@ -0,0 +1,4 @@
1
+
2
+ /src/.history
3
+ *.json
4
+ *.env
@@ -0,0 +1,5 @@
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
4
+ # Editor-based HTTP Client requests
5
+ /httpRequests/
@@ -0,0 +1 @@
1
+ penguin-agent
@@ -0,0 +1,6 @@
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="USE_PROJECT_PROFILE" value="false" />
4
+ <version value="1.0" />
5
+ </settings>
6
+ </component>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="MicroPythonTools">
4
+ <option name="activeWebReplPresetId" value="49e95b1f-e1d8-4cbd-bdfe-b41d2aa63ece" />
5
+ <option name="webReplPresets">
6
+ <map>
7
+ <entry key="49e95b1f-e1d8-4cbd-bdfe-b41d2aa63ece" value="{&quot;id&quot;:&quot;49e95b1f-e1d8-4cbd-bdfe-b41d2aa63ece&quot;,&quot;name&quot;:&quot;Default&quot;}" />
8
+ </map>
9
+ </option>
10
+ <option name="webReplPresetsMigrated" value="true" />
11
+ </component>
12
+ </project>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectRootManager" version="2" project-jdk-name="~/Dokumente/GitHub/penguin/.venv" project-jdk-type="Python SDK" />
4
+ </project>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/penguin.iml" filepath="$PROJECT_DIR$/.idea/penguin.iml" />
6
+ <module fileurl="file://$PROJECT_DIR$/.idea/penguin-agent-package.iml" filepath="$PROJECT_DIR$/.idea/penguin-agent-package.iml" />
7
+ </modules>
8
+ </component>
9
+ </project>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module external.system.id="pyproject.toml" type="PYTHON_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
6
+ <excludeFolder url="file://$MODULE_DIR$/.venv" />
7
+ </content>
8
+ <orderEntry type="jdk" jdkName="~/Dokumente/GitHub/penguin-agent/.venv" jdkType="Python SDK" />
9
+ <orderEntry type="sourceFolder" forTests="false" />
10
+ </component>
11
+ </module>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module external.system.id="pyproject.toml" type="PYTHON_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
6
+ <excludeFolder url="file://$MODULE_DIR$/.venv" />
7
+ </content>
8
+ <orderEntry type="jdk" jdkName="~/Dokumente/GitHub/penguin/.venv" jdkType="Python SDK" />
9
+ <orderEntry type="sourceFolder" forTests="false" />
10
+ </component>
11
+ </module>
@@ -0,0 +1,207 @@
1
+ Index: src/penguin/__main__.py
2
+ IDEA additional info:
3
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
4
+ <+>UTF-8
5
+ ===================================================================
6
+ diff --git a/src/penguin/__main__.py b/src/penguin-agent/__main__.py
7
+ rename from src/penguin/__main__.py
8
+ rename to src/penguin-agent/__main__.py
9
+ --- a/src/penguin/__main__.py (revision f229aff10363c0cd6431cf8c21ec24e207be1677)
10
+ +++ b/src/penguin-agent/__main__.py (date 1791565163364)
11
+ @@ -12,7 +12,7 @@
12
+ import json
13
+ import datetime
14
+ import os
15
+ -ollamaclient = Client(host="http://192.168.2.127:11434")
16
+ +ollamaclient = Client(host=os.environ['OLLAMA_BASE_URL'])
17
+ spinner = animation.Wait('spinner', text='penguin is working')
18
+
19
+ def execute_bash_command(command: str) -> str:
20
+ @@ -23,15 +23,24 @@
21
+
22
+ spinner.stop()
23
+ print("Using bash command: ")
24
+ - result = subprocess.run(command, cwd="/home/ferdinand", shell=True, capture_output=True, text=True)
25
+ try:
26
+ - print_formatted_text(HTML(f'<violet>{str(command)}</violet>'))
27
+ - print_formatted_text(HTML(f'<seagreen>{str(result.stdout.strip())}</seagreen>'))
28
+ - except:
29
+ - print('Printing command output was aborted. Command was executed')
30
+ + result = subprocess.run(command, check=True, cwd="/home/ferdinand", shell=True, capture_output=True, text=True)
31
+ + try:
32
+ + print_formatted_text(HTML(f'<violet>{str(command)}</violet>'))
33
+ + print_formatted_text(HTML(f'<seagreen>{str(result.stdout.strip())}</seagreen>'))
34
+ + except:
35
+ + print(command)
36
+ + print(result.stdout.strip())
37
+
38
+ - spinner.start()
39
+ - return result.stdout.strip()
40
+ + spinner.start()
41
+ + return result.stdout.strip()
42
+ + except subprocess.CalledProcessError as e:
43
+ + print(command)
44
+ + print('error')
45
+ + spinner.start()
46
+ + return e.stderr.strip()
47
+ +
48
+ +
49
+
50
+ def get_memory_entry(keyword: str) -> str:
51
+ """Get memory entries by keyword. The tool will return all the memory entries matching the keyword. Use this tool when searching for a solution for problem you already solved.
52
+ @@ -83,11 +92,8 @@
53
+ tool_dict = {"execute_bash_command": execute_bash_command, "enter_memory_entry": enter_memory_entry, "get_memory_entry": get_memory_entry}
54
+ messages_memory = [{'role': 'system', 'content': "You are a linux ai-assistant called penguin. You can execute bash commands and use your memory to help the user completiting tasks. ALWAYS provide a final answer WITHOUT tool calls to inform he user baout your results."}]
55
+ messages = []
56
+ -custom_style = Style.from_dict({
57
+ - '': 'bg:#2c3e50 fg:#ecf0f1', # Hintergrund dunkelblau, Text weiß
58
+ - 'prompt': 'bg:#2980b9 fg:#ffffff bold',
59
+ -})
60
+ -session = PromptSession(history=FileHistory(".history"), bottom_toolbar=bottom_toolbar, style=custom_style)
61
+ +
62
+ +session = PromptSession(history=FileHistory(".history"), bottom_toolbar=bottom_toolbar)
63
+ def run_agent(message, maximal_num_turns):
64
+ global messages_memory
65
+ global messages
66
+ @@ -137,7 +143,10 @@
67
+ print('Exiting penguin.')
68
+ break
69
+ else:
70
+ - typewrite(run_agent(user_input, 30), speed=0.01)
71
+ + try:
72
+ + typewrite(run_agent(user_input, 30), speed=0.01)
73
+ + except:
74
+ + print_formatted_text(HTML(f'<red>An error occurred during the agent loop. Please note that this project is currently in development and if it does not work, DO IT YOURSELF</red>'))
75
+
76
+ if __name__ == "__main__":
77
+ main()
78
+
79
+ Index: .gitignore
80
+ IDEA additional info:
81
+ Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
82
+ <+>\n/src/.history\n/src/penguin/memory.json\n
83
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
84
+ <+>UTF-8
85
+ ===================================================================
86
+ diff --git a/.gitignore b/.gitignore
87
+ --- a/.gitignore (revision f229aff10363c0cd6431cf8c21ec24e207be1677)
88
+ +++ b/.gitignore (date 1791563907743)
89
+ @@ -1,3 +1,4 @@
90
+
91
+ /src/.history
92
+ -/src/penguin/memory.json
93
+ +*.json
94
+ +*.env
95
+
96
+ Index: pyproject.toml
97
+ IDEA additional info:
98
+ Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
99
+ <+>[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"penguin\"\nversion = \"0.1.0\"\ndescription = \"Simple, minimalistic AI-Agent for Linux that runs on almost every device\"\nreadme = \"README.md\"\nrequires-python = \">=3.12\"\ndependencies = [\n \"animation>=0.0.7\",\n \"annotated-types>=0.8.0\",\n \"anyio>=4.15.1\",\n \"certifi>=2026.7.22\",\n \"ghostprint>=2.0.0\",\n \"h11>=0.16.0\",\n \"httpcore>=1.0.9\",\n \"httpx>=0.28.1\",\n \"idna>=3.20\",\n \"ollama>=0.6.2\",\n \"prompt_toolkit>=3.0.53\",\n \"pychalk>=2.0.1\",\n \"pydantic>=2.13.5\",\n \"pydantic_core>=2.46.5\",\n \"six>=1.17.0\",\n \"typing-inspection>=0.4.4\",\n \"typing_extensions>=4.16.0\",\n \"wcwidth>=0.9.1\",\n]\n
100
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
101
+ <+>UTF-8
102
+ ===================================================================
103
+ diff --git a/pyproject.toml b/pyproject.toml
104
+ --- a/pyproject.toml (revision f229aff10363c0cd6431cf8c21ec24e207be1677)
105
+ +++ b/pyproject.toml (date 1791572509991)
106
+ @@ -1,13 +1,22 @@
107
+ [build-system]
108
+ requires = ["hatchling"]
109
+ build-backend = "hatchling.build"
110
+ -
111
+ +[tool.hatch.build.targets.wheel]
112
+ +packages = ["src/penguin"]
113
+ [project]
114
+ -name = "penguin"
115
+ +name = "penguin-agent-package"
116
+ version = "0.1.0"
117
+ description = "Simple, minimalistic AI-Agent for Linux that runs on almost every device"
118
+ readme = "README.md"
119
+ requires-python = ">=3.12"
120
+ +classifiers = [
121
+ + "Programming Language :: Python :: 3",
122
+ + "Operating System :: OS Independent",
123
+ +]
124
+ +license = "MIT"
125
+ +authors = [
126
+ + { name="theobalduser", email="fzierenberg@icloud.com" },
127
+ +]
128
+ dependencies = [
129
+ "animation>=0.0.7",
130
+ "annotated-types>=0.8.0",
131
+ Index: .idea/penguin-agent.iml
132
+ IDEA additional info:
133
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
134
+ <+>UTF-8
135
+ ===================================================================
136
+ diff --git a/.idea/penguin-agent.iml b/.idea/penguin-agent.iml
137
+ new file mode 100644
138
+ --- /dev/null (date 1791563166628)
139
+ +++ b/.idea/penguin-agent.iml (date 1791563166628)
140
+ @@ -0,0 +1,11 @@
141
+ +<?xml version="1.0" encoding="UTF-8"?>
142
+ +<module external.system.id="pyproject.toml" type="PYTHON_MODULE" version="4">
143
+ + <component name="NewModuleRootManager">
144
+ + <content url="file://$MODULE_DIR$">
145
+ + <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
146
+ + <excludeFolder url="file://$MODULE_DIR$/.venv" />
147
+ + </content>
148
+ + <orderEntry type="jdk" jdkName="~/Dokumente/GitHub/penguin/.venv" jdkType="Python SDK" />
149
+ + <orderEntry type="sourceFolder" forTests="false" />
150
+ + </component>
151
+ +</module>
152
+
153
+ Index: .idea/modules.xml
154
+ IDEA additional info:
155
+ Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
156
+ <+><?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"ProjectModuleManager\">\n <modules>\n <module fileurl=\"file://$PROJECT_DIR$/.idea/penguin.iml\" filepath=\"$PROJECT_DIR$/.idea/penguin.iml\" />\n </modules>\n </component>\n</project>
157
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
158
+ <+>UTF-8
159
+ ===================================================================
160
+ diff --git a/.idea/modules.xml b/.idea/modules.xml
161
+ --- a/.idea/modules.xml (revision f229aff10363c0cd6431cf8c21ec24e207be1677)
162
+ +++ b/.idea/modules.xml (date 1791569063107)
163
+ @@ -3,6 +3,7 @@
164
+ <component name="ProjectModuleManager">
165
+ <modules>
166
+ <module fileurl="file://$PROJECT_DIR$/.idea/penguin.iml" filepath="$PROJECT_DIR$/.idea/penguin.iml" />
167
+ + <module fileurl="file://$PROJECT_DIR$/.idea/penguin@1.iml" filepath="$PROJECT_DIR$/.idea/penguin@1.iml" />
168
+ </modules>
169
+ </component>
170
+ </project>
171
+
172
+ Index: .idea/penguin@1.iml
173
+ IDEA additional info:
174
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
175
+ <+>UTF-8
176
+ ===================================================================
177
+ diff --git a/.idea/penguin@1.iml b/.idea/penguin@1.iml
178
+ new file mode 100644
179
+ --- /dev/null (date 1791569069440)
180
+ +++ b/.idea/penguin@1.iml (date 1791569069440)
181
+ @@ -0,0 +1,11 @@
182
+ +<?xml version="1.0" encoding="UTF-8"?>
183
+ +<module external.system.id="pyproject.toml" type="PYTHON_MODULE" version="4">
184
+ + <component name="NewModuleRootManager">
185
+ + <content url="file://$MODULE_DIR$">
186
+ + <sourceFolder url="file://$MODULE_DIR$/src" />
187
+ + <excludeFolder url="file://$MODULE_DIR$/.venv" />
188
+ + </content>
189
+ + <orderEntry type="jdk" jdkName="~/Dokumente/GitHub/penguin-agent/.venv" jdkType="Python SDK" />
190
+ + <orderEntry type="sourceFolder" forTests="false" />
191
+ + </component>
192
+ +</module>
193
+
194
+ Index: .idea/.name
195
+ IDEA additional info:
196
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
197
+ <+>UTF-8
198
+ ===================================================================
199
+ diff --git a/.idea/.name b/.idea/.name
200
+ new file mode 100644
201
+ --- /dev/null (date 1791568310002)
202
+ +++ b/.idea/.name (date 1791568310002)
203
+ @@ -0,0 +1,1 @@
204
+ +penguin-agent
205
+
206
+ diff --git a/src/penguin/memory.json b/src/penguin/memory.json
207
+ deleted file mode 100644
@@ -0,0 +1,4 @@
1
+ <changelist name="Uncommitted_changes_before_Update_at_09_10_26,_21_02_[Changes]" date="1791572531088" recycled="true" deleted="true">
2
+ <option name="PATH" value="$PROJECT_DIR$/.idea/shelf/Uncommitted_changes_before_Update_at_09_10_26,_21_02_[Changes]/shelved.patch" />
3
+ <option name="DESCRIPTION" value="Uncommitted changes before Update at 09.10.26, 21:02 [Changes]" />
4
+ </changelist>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,164 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="AutoImportSettings">
4
+ <option name="autoReloadType" value="SELECTIVE" />
5
+ </component>
6
+ <component name="ChangeListManager">
7
+ <list default="true" id="f864af2f-056a-4ed1-815d-52ec71aad92b" name="Changes" comment="README work - I'm sorry, I needed to test the gif rendering">
8
+ <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
9
+ </list>
10
+ <option name="SHOW_DIALOG" value="false" />
11
+ <option name="HIGHLIGHT_CONFLICTS" value="true" />
12
+ <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
13
+ <option name="LAST_RESOLUTION" value="IGNORE" />
14
+ </component>
15
+ <component name="Git.Settings">
16
+ <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
17
+ </component>
18
+ <component name="GitHubPullRequestSearchHistory">{
19
+ &quot;lastFilter&quot;: {
20
+ &quot;state&quot;: &quot;OPEN&quot;,
21
+ &quot;assignee&quot;: &quot;Test1-eu&quot;
22
+ }
23
+ }</component>
24
+ <component name="GithubPullRequestsUISettings">{
25
+ &quot;selectedUrlAndAccountId&quot;: {
26
+ &quot;url&quot;: &quot;https://github.com/Test1-eu/penguin.git&quot;,
27
+ &quot;accountId&quot;: &quot;64a69a75-d7ec-457f-ba10-9cc8d072f42c&quot;
28
+ }
29
+ }</component>
30
+ <component name="ProjectColorInfo">{
31
+ &quot;associatedIndex&quot;: 4,
32
+ &quot;fromUser&quot;: false
33
+ }</component>
34
+ <component name="ProjectId" id="3JuzjoRMtUd4O46bRYlP6Iupp1j" />
35
+ <component name="ProjectLevelVcsManager">
36
+ <ConfirmationsSetting value="2" id="Add" />
37
+ </component>
38
+ <component name="ProjectViewState">
39
+ <option name="hideEmptyMiddlePackages" value="true" />
40
+ <option name="showLibraryContents" value="true" />
41
+ </component>
42
+ <component name="PropertiesComponent">{
43
+ &quot;keyToString&quot;: {
44
+ &quot;ASKED_SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;,
45
+ &quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
46
+ &quot;Python.__main__.executor&quot;: &quot;Run&quot;,
47
+ &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
48
+ &quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
49
+ &quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;,
50
+ &quot;SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;,
51
+ &quot;codeWithMe.voiceChat.enabledByDefault&quot;: &quot;false&quot;,
52
+ &quot;git-widget-placeholder&quot;: &quot;main&quot;,
53
+ &quot;last_opened_file_path&quot;: &quot;/home/ferdinand/Dokumente/GitHub/penguin-agent&quot;,
54
+ &quot;node.js.detected.package.jshint&quot;: &quot;true&quot;,
55
+ &quot;node.js.selected.package.jshint&quot;: &quot;&quot;,
56
+ &quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
57
+ &quot;python.packages.toolwindow.project.migrated.to.right.top.v2&quot;: &quot;true&quot;,
58
+ &quot;settings.editor.selected.configurable&quot;: &quot;com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable&quot;
59
+ }
60
+ }</component>
61
+ <component name="RecentsManager">
62
+ <key name="MoveFile.RECENT_KEYS">
63
+ <recent name="$PROJECT_DIR$/src/penguin-agent" />
64
+ <recent name="$PROJECT_DIR$/src" />
65
+ <recent name="$PROJECT_DIR$" />
66
+ <recent name="$PROJECT_DIR$/src/penguin" />
67
+ </key>
68
+ </component>
69
+ <component name="SharedIndexes">
70
+ <attachedChunks>
71
+ <set>
72
+ <option value="bundled-python-sdk-73a66231627c-5bd6d13a2a75-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-262.10968.92" />
73
+ </set>
74
+ </attachedChunks>
75
+ </component>
76
+ <component name="TaskManager">
77
+ <task active="true" id="Default" summary="Default task">
78
+ <changelist id="f864af2f-056a-4ed1-815d-52ec71aad92b" name="Changes" comment="" />
79
+ <created>1790525890318</created>
80
+ <option name="number" value="Default" />
81
+ <option name="presentableId" value="Default" />
82
+ <updated>1790525890318</updated>
83
+ </task>
84
+ <task id="LOCAL-00001" summary="Updated the documentation tool(deleted it)">
85
+ <option name="closed" value="true" />
86
+ <created>1790526540788</created>
87
+ <option name="number" value="00001" />
88
+ <option name="presentableId" value="LOCAL-00001" />
89
+ <option name="project" value="LOCAL" />
90
+ <updated>1790526540788</updated>
91
+ </task>
92
+ <task id="LOCAL-00002" summary="Created PyPi package structure">
93
+ <option name="closed" value="true" />
94
+ <created>1790528840278</created>
95
+ <option name="number" value="00002" />
96
+ <option name="presentableId" value="LOCAL-00002" />
97
+ <option name="project" value="LOCAL" />
98
+ <updated>1790528840278</updated>
99
+ </task>
100
+ <task id="LOCAL-00003" summary="Updated module structure">
101
+ <option name="closed" value="true" />
102
+ <created>1791045044511</created>
103
+ <option name="number" value="00003" />
104
+ <option name="presentableId" value="LOCAL-00003" />
105
+ <option name="project" value="LOCAL" />
106
+ <updated>1791045044512</updated>
107
+ </task>
108
+ <task id="LOCAL-00004" summary="Updated module structure, added gitignore">
109
+ <option name="closed" value="true" />
110
+ <created>1791046451210</created>
111
+ <option name="number" value="00004" />
112
+ <option name="presentableId" value="LOCAL-00004" />
113
+ <option name="project" value="LOCAL" />
114
+ <updated>1791046451210</updated>
115
+ </task>
116
+ <task id="LOCAL-00005" summary="Reworked README.md and added .env structure to __main__.py">
117
+ <option name="closed" value="true" />
118
+ <created>1791575739009</created>
119
+ <option name="number" value="00005" />
120
+ <option name="presentableId" value="LOCAL-00005" />
121
+ <option name="project" value="LOCAL" />
122
+ <updated>1791575739009</updated>
123
+ </task>
124
+ <task id="LOCAL-00006" summary="README work">
125
+ <option name="closed" value="true" />
126
+ <created>1791652283259</created>
127
+ <option name="number" value="00006" />
128
+ <option name="presentableId" value="LOCAL-00006" />
129
+ <option name="project" value="LOCAL" />
130
+ <updated>1791652283259</updated>
131
+ </task>
132
+ <task id="LOCAL-00007" summary="README work">
133
+ <option name="closed" value="true" />
134
+ <created>1791652381136</created>
135
+ <option name="number" value="00007" />
136
+ <option name="presentableId" value="LOCAL-00007" />
137
+ <option name="project" value="LOCAL" />
138
+ <updated>1791652381136</updated>
139
+ </task>
140
+ <task id="LOCAL-00008" summary="README work - I'm sorry, I needed to test the gif rendering">
141
+ <option name="closed" value="true" />
142
+ <created>1791652475862</created>
143
+ <option name="number" value="00008" />
144
+ <option name="presentableId" value="LOCAL-00008" />
145
+ <option name="project" value="LOCAL" />
146
+ <updated>1791652475862</updated>
147
+ </task>
148
+ <option name="localTasksCounter" value="9" />
149
+ <servers />
150
+ </component>
151
+ <component name="TypeScriptGeneratedFilesManager">
152
+ <option name="version" value="3" />
153
+ </component>
154
+ <component name="VcsManagerConfiguration">
155
+ <MESSAGE value="Updated the documentation tool(deleted it)" />
156
+ <MESSAGE value="Created PyPi package structure" />
157
+ <MESSAGE value="Updated module structure" />
158
+ <MESSAGE value="Updated module structure, added gitignore" />
159
+ <MESSAGE value="Reworked README.md and added .env structure to __main__.py" />
160
+ <MESSAGE value="README work" />
161
+ <MESSAGE value="README work - I'm sorry, I needed to test the gif rendering" />
162
+ <option name="LAST_COMMIT_MESSAGE" value="README work - I'm sorry, I needed to test the gif rendering" />
163
+ </component>
164
+ </project>
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 theobaldsuperuser
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.
@@ -0,0 +1,82 @@
1
+ Metadata-Version: 2.5
2
+ Name: penguin-agent-package
3
+ Version: 0.1.0
4
+ Summary: Simple, minimalistic AI-Agent for Linux that runs on almost every device
5
+ Author-email: theobalduser <fzierenberg@icloud.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python :: 3
10
+ Requires-Python: >=3.12
11
+ Requires-Dist: animation>=0.0.7
12
+ Requires-Dist: annotated-types>=0.8.0
13
+ Requires-Dist: anyio>=4.15.1
14
+ Requires-Dist: certifi>=2026.7.22
15
+ Requires-Dist: dotenv>=1.2.4
16
+ Requires-Dist: ghostprint>=2.0.0
17
+ Requires-Dist: h11>=0.16.0
18
+ Requires-Dist: httpcore>=1.0.9
19
+ Requires-Dist: httpx>=0.28.1
20
+ Requires-Dist: idna>=3.20
21
+ Requires-Dist: ollama>=0.6.2
22
+ Requires-Dist: prompt-toolkit>=3.0.53
23
+ Requires-Dist: pychalk>=2.0.1
24
+ Requires-Dist: pydantic-core>=2.46.5
25
+ Requires-Dist: pydantic>=2.13.5
26
+ Requires-Dist: six>=1.17.0
27
+ Requires-Dist: typing-extensions>=4.16.0
28
+ Requires-Dist: typing-inspection>=0.4.4
29
+ Requires-Dist: wcwidth>=0.9.1
30
+ Description-Content-Type: text/markdown
31
+
32
+ # penguin
33
+ Simple, minimalist AI-Agent for Linux that runs on almost every device
34
+
35
+ ![Alt Text](https://github.com/Test1-eu/penguin-agent/blob/main/ressources/bildschirmaufzeichnung-vom-2026-09-26-23-34-26_q3iOhA72%20(online-video-cutter.com).gif)
36
+
37
+ ## LLM
38
+ Penguin uses (at the moment) the qwen3.5:2b model with Ollama as inference. The model can run at a good conversation speed on most modern laptops and a bit slower on older machines, the minimum I reached with an eight years old Lenovo Thinkpad(i5, 7th generation?) was 7 t/s. I recommend using an 32k context window, which is enough for most applications you will run with penguin.
39
+
40
+ ## Installing & Quick Start
41
+ ### One-line install command using pipx:
42
+ [pipx](https://github.com/pypa/pipx) is a modern PyPi Installer for CLI applications - like apt but for python. Use it to install the ```penguin-agent-package``` with a systemwide command ```penguin``` :
43
+
44
+ ```
45
+ sudo pipx install --global --program-name penguin penguin-agent-package
46
+ ```
47
+
48
+ Then type ```penguin``` in your terminal
49
+ ### Standard install using pip:
50
+ [pip](https://github.com/pypa/pip) is the standard PyPi Installer. Install ```penguin-agent-package``` using pip in a virtual environment:
51
+
52
+ ```
53
+ pip install penguin-agent-package
54
+ ```
55
+
56
+
57
+ >[!WARNING]
58
+ >This will not install the systemwide ```penguin``` command. You need to add it manually to PATH.
59
+
60
+ ## Features
61
+ Penguin is a very, very simple tool:
62
+ + Uses an Ollama backend(see [LLM](#llm))
63
+ + Three basic tools in the agent-loop:
64
+ * execute_bash_command
65
+ * get_memory_entry
66
+ * enter_memory_entry
67
+ + Executes bash commands always at ```~```
68
+ + Uses [prompt_toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) to manage sessions with history and suggestions
69
+ + Uses memory.json file for managing the agent memory
70
+
71
+ ## Run it
72
+ Just type:
73
+
74
+ ```
75
+ penguin
76
+ ```
77
+
78
+ ## How it works
79
+ The choice of Ollama is due entirely to its good tool-calling integration in python. Tool schemes are converted by the ollama library to JSON and passed to the native function calling of the model.
80
+
81
+ ## Credits
82
+ I heavily used the prompt_toolkit library for the design and the interaction with the tool. Thanks also to the amazing ollama python library that makes it so easy to use LLMs with Python.
@@ -0,0 +1,51 @@
1
+ # penguin
2
+ Simple, minimalist AI-Agent for Linux that runs on almost every device
3
+
4
+ ![Alt Text](https://github.com/Test1-eu/penguin-agent/blob/main/ressources/bildschirmaufzeichnung-vom-2026-09-26-23-34-26_q3iOhA72%20(online-video-cutter.com).gif)
5
+
6
+ ## LLM
7
+ Penguin uses (at the moment) the qwen3.5:2b model with Ollama as inference. The model can run at a good conversation speed on most modern laptops and a bit slower on older machines, the minimum I reached with an eight years old Lenovo Thinkpad(i5, 7th generation?) was 7 t/s. I recommend using an 32k context window, which is enough for most applications you will run with penguin.
8
+
9
+ ## Installing & Quick Start
10
+ ### One-line install command using pipx:
11
+ [pipx](https://github.com/pypa/pipx) is a modern PyPi Installer for CLI applications - like apt but for python. Use it to install the ```penguin-agent-package``` with a systemwide command ```penguin``` :
12
+
13
+ ```
14
+ sudo pipx install --global --program-name penguin penguin-agent-package
15
+ ```
16
+
17
+ Then type ```penguin``` in your terminal
18
+ ### Standard install using pip:
19
+ [pip](https://github.com/pypa/pip) is the standard PyPi Installer. Install ```penguin-agent-package``` using pip in a virtual environment:
20
+
21
+ ```
22
+ pip install penguin-agent-package
23
+ ```
24
+
25
+
26
+ >[!WARNING]
27
+ >This will not install the systemwide ```penguin``` command. You need to add it manually to PATH.
28
+
29
+ ## Features
30
+ Penguin is a very, very simple tool:
31
+ + Uses an Ollama backend(see [LLM](#llm))
32
+ + Three basic tools in the agent-loop:
33
+ * execute_bash_command
34
+ * get_memory_entry
35
+ * enter_memory_entry
36
+ + Executes bash commands always at ```~```
37
+ + Uses [prompt_toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) to manage sessions with history and suggestions
38
+ + Uses memory.json file for managing the agent memory
39
+
40
+ ## Run it
41
+ Just type:
42
+
43
+ ```
44
+ penguin
45
+ ```
46
+
47
+ ## How it works
48
+ The choice of Ollama is due entirely to its good tool-calling integration in python. Tool schemes are converted by the ollama library to JSON and passed to the native function calling of the model.
49
+
50
+ ## Credits
51
+ I heavily used the prompt_toolkit library for the design and the interaction with the tool. Thanks also to the amazing ollama python library that makes it so easy to use LLMs with Python.
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+ [tool.hatch.build.targets.wheel]
5
+ packages = ["src/penguin"]
6
+ [project]
7
+ name = "penguin-agent-package"
8
+ version = "0.1.0"
9
+ description = "Simple, minimalistic AI-Agent for Linux that runs on almost every device"
10
+ readme = "README.md"
11
+ requires-python = ">=3.12"
12
+ classifiers = [
13
+ "Programming Language :: Python :: 3",
14
+ "Operating System :: OS Independent",
15
+ ]
16
+ license = "MIT"
17
+ authors = [
18
+ { name="theobalduser", email="fzierenberg@icloud.com" },
19
+ ]
20
+ dependencies = [
21
+ "animation>=0.0.7",
22
+ "annotated-types>=0.8.0",
23
+ "anyio>=4.15.1",
24
+ "certifi>=2026.7.22",
25
+ "ghostprint>=2.0.0",
26
+ "h11>=0.16.0",
27
+ "httpcore>=1.0.9",
28
+ "httpx>=0.28.1",
29
+ "idna>=3.20",
30
+ "ollama>=0.6.2",
31
+ "prompt_toolkit>=3.0.53",
32
+ "pychalk>=2.0.1",
33
+ "pydantic>=2.13.5",
34
+ "pydantic_core>=2.46.5",
35
+ "six>=1.17.0",
36
+ "typing-inspection>=0.4.4",
37
+ "typing_extensions>=4.16.0",
38
+ "wcwidth>=0.9.1",
39
+ "dotenv>=1.2.4"
40
+ ]
@@ -0,0 +1,20 @@
1
+ animation>=0.0.7
2
+ annotated-types>=0.8.0
3
+ anyio>=4.15.1
4
+ certifi>=2026.7.22
5
+ ghostprint>=2.0.0
6
+ h11>=0.16.0
7
+ httpcore>=1.0.9
8
+ httpx>=0.28.1
9
+ idna>=3.20
10
+ ollama>=0.6.2
11
+ prompt_toolkit>=3.0.53
12
+ pychalk>=2.0.1
13
+ pydantic>=2.13.5
14
+ pydantic_core>=2.46.5
15
+ six>=1.17.0
16
+ typing-inspection>=0.4.4
17
+ typing_extensions>=4.16.0
18
+ wcwidth>=0.9.1
19
+
20
+ python-dotenv
@@ -0,0 +1,159 @@
1
+ from ollama import Client
2
+ from dotenv import load_dotenv
3
+ from prompt_toolkit import print_formatted_text
4
+ from prompt_toolkit.completion import WordCompleter
5
+ from prompt_toolkit.formatted_text import *
6
+ from prompt_toolkit.shortcuts import PromptSession
7
+ from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
8
+ from prompt_toolkit.history import FileHistory
9
+ from prompt_toolkit.styles import Style
10
+ from ghostprint import typewrite
11
+ import animation
12
+ import subprocess
13
+ import json
14
+ import datetime
15
+ import os
16
+
17
+ load_dotenv()
18
+ print(os.getenv("OLLAMA_BASE_URL"))
19
+ if os.getenv('OLLAMA_BASE_URL') is None:
20
+ print('No OLLAMA_BASE_URL found.')
21
+ baseurl = input('Please enter your ollama base URL: ')
22
+ with open('.env', 'w') as file:
23
+ file.write('OLLAMA_BASE_URL=' + baseurl)
24
+ load_dotenv()
25
+ ollamaclient = Client(host=os.getenv('OLLAMA_BASE_URL'))
26
+
27
+ spinner = animation.Wait('spinner', text='penguin is working')
28
+
29
+
30
+ def execute_bash_command(command: str) -> str:
31
+ """Execute bash commands in a linux shell environnemnt. Give command as full string.
32
+ Args:
33
+ command -> str
34
+ """
35
+
36
+ spinner.stop()
37
+ print("Using bash command: ")
38
+ try:
39
+ result = subprocess.run(command, check=True, cwd="/home/ferdinand", shell=True, capture_output=True, text=True)
40
+ try:
41
+ print_formatted_text(HTML(f'<violet>{str(command)}</violet>'))
42
+ print_formatted_text(HTML(f'<seagreen>{str(result.stdout.strip())}</seagreen>'))
43
+ except:
44
+ print(command)
45
+ print(result.stdout.strip())
46
+
47
+ spinner.start()
48
+ return result.stdout.strip()
49
+ except subprocess.CalledProcessError as e:
50
+ print(command)
51
+ print('error')
52
+ spinner.start()
53
+ return e.stderr.strip()
54
+
55
+ def get_memory_entry(keyword: str) -> str:
56
+ """Get memory entries by keyword. The tool will return all the memory entries matching the keyword. Use this tool when searching for a solution for problem you already solved.
57
+ Args:
58
+ keyword -> str
59
+ """
60
+ spinner.stop()
61
+ print("Using get_memory_entry()")
62
+ print(keyword)
63
+ spinner.start()
64
+ matching_entries = []
65
+ with open("memory.json", "r+") as file:
66
+ data = json.load(file)
67
+ for entry in data["memory_entries"]:
68
+ if len(set(keyword.split()).intersection(set(entry["keyword"].split()))) != 0 or len(set(keyword.split()).intersection(set(entry["entry"].split()))) != 0:
69
+ matching_entries.append(entry)
70
+ file.seek(0)
71
+ json.dump(data, file, indent=4)
72
+ return str(matching_entries)
73
+
74
+ def enter_memory_entry(keyword: str, entry: str) -> str:
75
+ """Enter memory entry with keyword and entry. The tool will enter your memory entry. Use when solved a relevant problem to remember the solution. The keyword is to refind it later, so keep it clear. You may give your entry in the entry argument.
76
+ Args:
77
+ keyword -> str
78
+ entry -> str
79
+ """
80
+ spinner.stop()
81
+ print("Using enter_memory_entry()")
82
+ print(keyword, entry)
83
+ spinner.start()
84
+ entryjson = {"timestamp": datetime.datetime.now(), "keyword": keyword, "entry": entry}
85
+ try:
86
+ with open("memory.json", "r", encoding="utf-8") as file:
87
+ data = json.load(file)
88
+ except (FileNotFoundError, json.JSONDecodeError):
89
+ data = {"memory_entries": []}
90
+ data["memory_entries"].append(entryjson)
91
+ with open("memory.json.tmp", "w", encoding="utf-8") as temp_file:
92
+ json.dump(data, temp_file, indent=4, ensure_ascii=False, default=str)
93
+ os.replace("memory.json.tmp", "memory.json")
94
+ print("Successfully updated file safely!")
95
+
96
+ return str([keyword, entry])
97
+
98
+ def bottom_toolbar():
99
+ return HTML('This may not work... <b><style bg="ansired">DO IT YOURSELF</style></b>!')
100
+
101
+ list_of_tools = ["execute_bash_command", "get_memory_entry", "enter_memory_entry"]
102
+ tool_dict = {"execute_bash_command": execute_bash_command, "enter_memory_entry": enter_memory_entry, "get_memory_entry": get_memory_entry}
103
+ messages_memory = [{'role': 'system', 'content': "You are a linux ai-assistant called penguin. You can execute bash commands and use your memory to help the user completiting tasks. ALWAYS provide a final answer WITHOUT tool calls to inform he user baout your results."}]
104
+ messages = []
105
+
106
+ session = PromptSession(history=FileHistory(".history"), bottom_toolbar=bottom_toolbar)
107
+ def run_agent(message, maximal_num_turns):
108
+ global messages_memory
109
+ global messages
110
+ messages = []
111
+ messages_memory.append({'role': 'user', 'content': message})
112
+ messages = messages_memory
113
+ spinner.start()
114
+ for turn_number in range(maximal_num_turns):
115
+ response = ollamaclient.chat(model="qwen3.5:2b_context", messages=messages, tools=[execute_bash_command, get_memory_entry, enter_memory_entry])
116
+
117
+ message_out = response["message"]
118
+
119
+ messages.append(message_out)
120
+ messages_memory.append(message_out)
121
+ tool_calls = message_out.get("tool_calls") or []
122
+ if not tool_calls:
123
+ spinner.stop()
124
+ return message_out["content"]
125
+ for call in tool_calls:
126
+ function_name = call["function"]["name"]
127
+ function_args_list = call["function"]["arguments"]
128
+ if function_name not in list_of_tools:
129
+ spinner.stop()
130
+ result = json.dumps({'error': f'Unknown tool or function: {function_name}'})
131
+ else:
132
+ result = tool_dict[function_name](**function_args_list)
133
+ messages.append({'role': 'tool', 'name': function_name, 'content': result})
134
+ messages_memory.append({'role': 'tool', 'name': function_name, 'content': result})
135
+
136
+ spinner.stop()
137
+
138
+ return "No final answer: running out of turns."
139
+
140
+ style = Style.from_dict(
141
+ {
142
+ "frame.border": "#893333",
143
+ }
144
+ )
145
+
146
+
147
+ def main():
148
+ os.system('cls' if os.name == 'nt' else 'clear')
149
+ completer = WordCompleter(['/exit'], ignore_case=True)
150
+ while True:
151
+ user_input = session.prompt(HTML('<ansired>penguin></ansired> '), completer=completer, show_frame=True, style=style, auto_suggest=AutoSuggestFromHistory(), enable_history_search=True)
152
+ if user_input == "/exit":
153
+ print('Exiting penguin.')
154
+ break
155
+ else:
156
+ typewrite(run_agent(user_input, 30), speed=0.01)
157
+
158
+ if __name__ == "__main__":
159
+ main()