janito 1.2.0__py3-none-any.whl → 1.3.0__py3-none-any.whl
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.
- janito/__init__.py +1 -1
- janito/agent/agent.py +27 -10
- janito/agent/config.py +37 -9
- janito/agent/config_utils.py +9 -0
- janito/agent/conversation.py +19 -3
- janito/agent/runtime_config.py +1 -0
- janito/agent/tool_handler.py +154 -52
- janito/agent/tools/__init__.py +9 -8
- janito/agent/tools/ask_user.py +2 -1
- janito/agent/tools/fetch_url.py +27 -35
- janito/agent/tools/file_ops.py +72 -0
- janito/agent/tools/find_files.py +47 -26
- janito/agent/tools/get_lines.py +58 -0
- janito/agent/tools/py_compile.py +26 -0
- janito/agent/tools/python_exec.py +47 -0
- janito/agent/tools/remove_directory.py +38 -0
- janito/agent/tools/replace_text_in_file.py +67 -0
- janito/agent/tools/run_bash_command.py +134 -0
- janito/agent/tools/search_files.py +52 -0
- janito/cli/_print_config.py +12 -12
- janito/cli/arg_parser.py +6 -1
- janito/cli/config_commands.py +56 -8
- janito/cli/runner.py +21 -9
- janito/cli_chat_shell/chat_loop.py +5 -3
- janito/cli_chat_shell/commands.py +34 -37
- janito/cli_chat_shell/config_shell.py +1 -1
- janito/cli_chat_shell/load_prompt.py +1 -1
- janito/cli_chat_shell/session_manager.py +11 -15
- janito/cli_chat_shell/ui.py +17 -8
- janito/render_prompt.py +3 -1
- janito/web/app.py +76 -19
- janito-1.3.0.dist-info/METADATA +142 -0
- janito-1.3.0.dist-info/RECORD +51 -0
- janito/agent/tools/bash_exec.py +0 -58
- janito/agent/tools/create_directory.py +0 -19
- janito/agent/tools/create_file.py +0 -43
- janito/agent/tools/file_str_replace.py +0 -48
- janito/agent/tools/move_file.py +0 -37
- janito/agent/tools/remove_file.py +0 -19
- janito/agent/tools/search_text.py +0 -41
- janito/agent/tools/view_file.py +0 -34
- janito/templates/system_instructions.j2 +0 -38
- janito-1.2.0.dist-info/METADATA +0 -85
- janito-1.2.0.dist-info/RECORD +0 -51
- {janito-1.2.0.dist-info → janito-1.3.0.dist-info}/WHEEL +0 -0
- {janito-1.2.0.dist-info → janito-1.3.0.dist-info}/entry_points.txt +0 -0
- {janito-1.2.0.dist-info → janito-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {janito-1.2.0.dist-info → janito-1.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: janito
|
3
|
+
Version: 1.3.0
|
4
|
+
Summary: A Natural Programming Language Agent,
|
5
|
+
Author-email: João Pinto <joao.pinto@gmail.com>
|
6
|
+
License: MIT
|
7
|
+
Project-URL: homepage, https://github.com/joaompinto/janito
|
8
|
+
Project-URL: repository, https://github.com/joaompinto/janito
|
9
|
+
Keywords: agent,framework,tools,automation
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
12
|
+
Classifier: Operating System :: OS Independent
|
13
|
+
Requires-Python: >=3.10
|
14
|
+
Description-Content-Type: text/markdown
|
15
|
+
License-File: LICENSE
|
16
|
+
Requires-Dist: beautifulsoup4
|
17
|
+
Requires-Dist: flask
|
18
|
+
Requires-Dist: jinja2
|
19
|
+
Requires-Dist: openai
|
20
|
+
Requires-Dist: pathspec
|
21
|
+
Requires-Dist: prompt_toolkit
|
22
|
+
Requires-Dist: requests
|
23
|
+
Requires-Dist: rich
|
24
|
+
Dynamic: license-file
|
25
|
+
|
26
|
+
# 🚀 Janito: Agent
|
27
|
+
|
28
|
+
Janito is an AI-powered assistant for the command line and web that interprets natural language instructions to edit code, manage files, and analyze projects using patterns and tools designed by experienced software engineers. It prioritizes transparency, interactive clarification, and precise, reviewable changes.
|
29
|
+
|
30
|
+
---
|
31
|
+
|
32
|
+
## ⚡ Quick Start
|
33
|
+
|
34
|
+
Run a one-off prompt:
|
35
|
+
```bash
|
36
|
+
python -m janito "Refactor the data processing module to improve readability."
|
37
|
+
```
|
38
|
+
|
39
|
+
Or start the interactive chat shell:
|
40
|
+
```bash
|
41
|
+
python -m janito
|
42
|
+
```
|
43
|
+
|
44
|
+
Launch the web UI:
|
45
|
+
```bash
|
46
|
+
python -m janito.web
|
47
|
+
```
|
48
|
+
|
49
|
+
---
|
50
|
+
|
51
|
+
## ✨ Key Features
|
52
|
+
- 📝 **Code Editing via Natural Language:** Modify, create, or delete code files simply by describing the changes.
|
53
|
+
- 📁 **File & Directory Management:** Navigate, create, move, or remove files and folders.
|
54
|
+
- 🧠 **Context-Aware:** Understands your project structure for precise edits.
|
55
|
+
- 💬 **Interactive User Prompts:** Asks for clarification when needed.
|
56
|
+
- 🧩 **Extensible Tooling:** Built-in tools for file operations, shell commands, directory listing, Python file validation, text replacement, code execution, and more. Recent tools include:
|
57
|
+
- `find_files`: Search for files matching a pattern in directories.
|
58
|
+
- `get_lines`: Retrieve specific lines from files for efficient context.
|
59
|
+
- `py_compile_file`: Validate Python files for syntax correctness.
|
60
|
+
- `replace_text_in_file`: Replace exact text fragments in files.
|
61
|
+
- `search_files`: Search for text patterns across files.
|
62
|
+
- `python_exec`: Execute Python code and capture output.
|
63
|
+
- And more, see `janito/agent/tools/` for the full list.
|
64
|
+
- 🌐 **Web Interface (In Development):** Upcoming simple web UI for streaming responses and tool progress.
|
65
|
+
|
66
|
+
---
|
67
|
+
|
68
|
+
|
69
|
+
## 📦 Installation
|
70
|
+
|
71
|
+
### Requirements
|
72
|
+
- Python 3.10+
|
73
|
+
|
74
|
+
...
|
75
|
+
|
76
|
+
### Configuration & CLI Options
|
77
|
+
|
78
|
+
Below are the supported configuration parameters and CLI flags. Some options can be set via config files, CLI flags, or both. Use `python -m janito --help` for a full list, or `python -m janito --help-config` to see all config keys and their descriptions.
|
79
|
+
|
80
|
+
| Key / Flag | Description | How to set | Default |
|
81
|
+
|---------------------------|---------------------------------------------------------------------------------------------|-----------------------------------------------------------------|--------------------------------------------|
|
82
|
+
| `api_key` | API key for OpenAI-compatible service | `--set-api-key`, config file | _None_ (required) |
|
83
|
+
| `model` | Model name to use for this session | `--model` (session only), `--set-local-config model=...`, or `--set-global-config` | `openai/gpt-4.1` |
|
84
|
+
| `base_url` | API base URL (OpenAI-compatible endpoint) | `--set-local-config base_url=...` or `--set-global-config` | `https://openrouter.ai/api/v1` |
|
85
|
+
| `role` | Role description for the system prompt | `--role` or config | "software engineer" |
|
86
|
+
| `system_prompt` | Override the entire system prompt as a raw string | `--system-prompt` or config | _Default prompt_ |
|
87
|
+
| `system_file` | Use a plain text file as the system prompt (takes precedence over `system_prompt`) | `--system-file` (CLI only) | _None_ |
|
88
|
+
| `temperature` | Sampling temperature (float, e.g., 0.0 - 2.0) | `--temperature` or config | 0.2 |
|
89
|
+
| `max_tokens` | Maximum tokens for model response | `--max-tokens` or config | 200000 |
|
90
|
+
| `max_rounds` | Maximum number of agent rounds per prompt/session | `--max-rounds` or config | 50 |
|
91
|
+
| `max_tools` | Maximum number of tool calls allowed within a chat session | `--max-tools` or config | _None_ (unlimited) |
|
92
|
+
| `no_tools` | Disable tool use (no tools passed to agent) | `-n`, `--no-tools` (CLI only) | False |
|
93
|
+
| `trust` | Trust mode: suppresses run_bash_command output, only shows output file locations | `--trust` (CLI only) | False |
|
94
|
+
| `template` / `template.*` | Template context dictionary for prompt rendering (nested or flat keys) | Config only | _None_ |
|
95
|
+
|
96
|
+
Other config-related CLI flags:
|
97
|
+
|
98
|
+
- `--set-local-config key=val` Set a local config value
|
99
|
+
- `--set-global-config key=val` Set a global config value
|
100
|
+
- `--run-config key=val` Set a runtime (in-memory only) config value (can be repeated)
|
101
|
+
- `--show-config` Show effective configuration and exit
|
102
|
+
- `--config-reset-local` Remove the local config file
|
103
|
+
- `--config-reset-global` Remove the global config file
|
104
|
+
- `--set-api-key KEY` Set and save the API key globally
|
105
|
+
- `--help-config` Show all configuration options and exit
|
106
|
+
|
107
|
+
Session & shell options:
|
108
|
+
|
109
|
+
- `--continue-session` Continue from the last saved conversation
|
110
|
+
- `--web` Launch the Janito web server instead of CLI
|
111
|
+
|
112
|
+
Verbose/debugging flags:
|
113
|
+
|
114
|
+
- `--verbose-http` Enable verbose HTTP logging
|
115
|
+
- `--verbose-http-raw` Enable raw HTTP wire-level logging
|
116
|
+
- `--verbose-response` Pretty print the full response object
|
117
|
+
- `--verbose-tools` Print tool call parameters and results
|
118
|
+
- `--show-system` Show model, parameters, system prompt, and tool definitions, then exit
|
119
|
+
- `--version` Show program's version number and exit
|
120
|
+
|
121
|
+
|
122
|
+
|
123
|
+
---
|
124
|
+
|
125
|
+
## 🧩 System Prompt & Role
|
126
|
+
|
127
|
+
Janito operates using a system prompt template that defines its behavior, communication style, and capabilities. By default, Janito assumes the role of a "software engineer"—this means its responses and actions are tailored to the expectations and best practices of professional software engineering.
|
128
|
+
|
129
|
+
- **Role:** You can customize the agent's role (e.g., "data scientist", "DevOps engineer") using the `--role` flag or config. The default is `software engineer`.
|
130
|
+
- **System Prompt Template:** The system prompt is rendered from a Jinja2 template (see `janito/agent/templates/system_instructions.j2` (now located directly under the agent directory)). This template governs how the agent interprets instructions, interacts with files, and communicates with users.
|
131
|
+
- **Customization:** Advanced users can override the system prompt with the `--system-prompt` flag (raw string), or point to a custom file using `--system-file`.
|
132
|
+
|
133
|
+
The default template ensures the agent:
|
134
|
+
- Prioritizes safe, reviewable, and minimal changes
|
135
|
+
- Asks for clarification when instructions are ambiguous
|
136
|
+
- Provides concise plans before taking action
|
137
|
+
- Documents any changes made
|
138
|
+
|
139
|
+
For more details or to customize the prompt, see the template file at `janito/agent/templates/system_instructions.j2`.
|
140
|
+
|
141
|
+
---
|
142
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
janito/__init__.py,sha256=N53F87jnDXnPWTxIl8Act-J_9sVbOVV8kluLQav-f9o,23
|
2
|
+
janito/__main__.py,sha256=CBScR30Tm-vuhIJM8o5HXKr0q-smICiwSVyuU68BP8U,78
|
3
|
+
janito/render_prompt.py,sha256=xCQgYRqMyz9Pzi7096NoZfC4lFiKEHEaXiTYP6ByViY,513
|
4
|
+
janito/agent/__init__.py,sha256=CByAH5Yk-yH64zo0RU7Z3nsn_7Vmandphqk0JNlpyj8,21
|
5
|
+
janito/agent/agent.py,sha256=X5swt-SUKnIxrSQaGfhxPk61M-BXREz4n02ynE0dVVU,4245
|
6
|
+
janito/agent/config.py,sha256=uGjqQPLQZxzQ6JdpmgRdA1g0g-R7zeHLwidQI78JSJw,4392
|
7
|
+
janito/agent/config_defaults.py,sha256=Ow-LKq88MmMTQ6LDH_u26NqJ8-db35KpcfR8FYuWGBw,363
|
8
|
+
janito/agent/config_utils.py,sha256=UmvR236wDrMc-aTy9LxVbop6YeoJaaPb1d2DBMlkSRg,254
|
9
|
+
janito/agent/conversation.py,sha256=vbCL-1LlJjKRN6q3CjfSe4ENGplP-YQKsAEA1uX3RWU,6184
|
10
|
+
janito/agent/queued_tool_handler.py,sha256=THPymKXnpoXfN49EhW5b4hrwpWZZup73JKFDJ_U03tI,540
|
11
|
+
janito/agent/runtime_config.py,sha256=H6bnRVYR0zC-WhmLpBU-IXikXWKcIiBQqFnc4uGooj4,908
|
12
|
+
janito/agent/tool_handler.py,sha256=sKnySRfZQBUjZxve8CH74me9Q4_ewtIege_HBiCs9wY,8291
|
13
|
+
janito/agent/tools/__init__.py,sha256=j0MGKIrtEkI3f6yumsJeDqiPyvDMehZ_3e2Osujev3c,486
|
14
|
+
janito/agent/tools/ask_user.py,sha256=-omTj2woHGbl4xR14sB3dbnRD1zGIbvKv4wjq9g4NXE,1917
|
15
|
+
janito/agent/tools/fetch_url.py,sha256=tce4L5LDiIsSreo-aJVmpwu1yCkDCY0kEZId3lgGiCU,1578
|
16
|
+
janito/agent/tools/file_ops.py,sha256=vu3DttsaPfqBX82-UhPY079CmMXFDuaCXtre5kOXKW0,3171
|
17
|
+
janito/agent/tools/find_files.py,sha256=OxJghgmPvk3RFF5N_6CCW_ftB6RgG3QzeBzc1bItLKA,2353
|
18
|
+
janito/agent/tools/get_lines.py,sha256=eazwPkwB_P9VkHN-ubOZK5lK5qOJGU0QXVOb6RwQl3g,2948
|
19
|
+
janito/agent/tools/gitignore_utils.py,sha256=zXiqx4HetZ7iKkV5qXyHyZ5yrrVex4G17WHBWToFo3Y,1158
|
20
|
+
janito/agent/tools/py_compile.py,sha256=bQ6P0Vr8FBZQ_hqNjIEylJIzeiH2aggRjNW_WCgG7sA,1042
|
21
|
+
janito/agent/tools/python_exec.py,sha256=C4qG6Um9RsnpHqii1Z3mVFp3qXnaL87Ns0mJSeBgOJc,1769
|
22
|
+
janito/agent/tools/remove_directory.py,sha256=CUzXaotktgpyX6LUsfK_-m91Onkwk9wbdAZcUayt2jw,1727
|
23
|
+
janito/agent/tools/replace_text_in_file.py,sha256=2uOZJW2SzZPFp7_wcU8Hw38pKWDWkmq_24mxiUrRWDo,3533
|
24
|
+
janito/agent/tools/rich_live.py,sha256=cuZ3-ZxpuHxR1TvIcp0bi9F3QM1M6Ms0XiOMd8If8gU,1161
|
25
|
+
janito/agent/tools/rich_utils.py,sha256=aQMqeaq3hIpzZ5EHQBNTKS5dNsojQp9MDfJSoqOQe0k,837
|
26
|
+
janito/agent/tools/run_bash_command.py,sha256=AUswjRcVHODhHQtx1fH3zQqQjSftolxAwhNzAtnj8BY,6020
|
27
|
+
janito/agent/tools/search_files.py,sha256=HbLq9oHcb9udrbql6-QE53zo5F3UeuhlptMvf2mIQVo,1945
|
28
|
+
janito/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
|
+
janito/cli/_print_config.py,sha256=vMMlCY-hJPsLHUxx9TJuOY9iyYVBH8C4nVACCwY4l5U,3294
|
30
|
+
janito/cli/_utils.py,sha256=Q_OCFZmbr78qW4hSSUUhjondVc0ao7-iUHE7Ig8IP1g,289
|
31
|
+
janito/cli/arg_parser.py,sha256=4dSxHk02VD2_-utR5XCDt6Xs7tsye2HeQmTbaJiWFME,3724
|
32
|
+
janito/cli/config_commands.py,sha256=kYZ_SlHkZRlg-hLAdYKj50qnJgPCZUbW7zU4FykE7XQ,8098
|
33
|
+
janito/cli/logging_setup.py,sha256=dWQ0wFf4YuF5lxZlhpN6lis7G56LeFYUwQdh9nA5NmA,1141
|
34
|
+
janito/cli/main.py,sha256=ONmn_lIPu8_Rd57j3YfWEx46fWj8gAkONPLdctABwy0,1333
|
35
|
+
janito/cli/runner.py,sha256=64BErYxTvJjzFPKpbQvOoq6ARiEbPp03ZGFZBVogkgg,5043
|
36
|
+
janito/cli_chat_shell/__init__.py,sha256=PDGl7xK_vgkROoXvUxGZqVQFfuL9U4TjdexpP8E2JKg,41
|
37
|
+
janito/cli_chat_shell/chat_loop.py,sha256=AcryIgXb36g6214XKj75Xx4VxOYG-i8_IC_ROZyOLdk,5516
|
38
|
+
janito/cli_chat_shell/commands.py,sha256=ytt_5dym2sKOIlTjwAmfMg2jQVBlenDbPNwKJasPsDg,7245
|
39
|
+
janito/cli_chat_shell/config_shell.py,sha256=WlOxt7YOTIJluD3hURXbtOSodPHPnCwcO0LaH8_XXJE,3301
|
40
|
+
janito/cli_chat_shell/load_prompt.py,sha256=B7C_IhMk3n1XBQcfOwFVDnjrj1278JL3kV4UtkeeZx8,604
|
41
|
+
janito/cli_chat_shell/session_manager.py,sha256=PdqmFw4jZaCtQpazjgkKk_yhleZrzZOz_QqFeIf9Km4,1818
|
42
|
+
janito/cli_chat_shell/ui.py,sha256=9IngH8HFherjatfa9Eu37jb4b2gOP0lsgcUhbnQKCZA,5557
|
43
|
+
janito/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
44
|
+
janito/web/__main__.py,sha256=oPXNF332aCeI7aUWr7_8M57oOKugw422VrEubxFp0P4,354
|
45
|
+
janito/web/app.py,sha256=stogs_HaM-axsWTwoiVma9M46Y8dMu0QRjopsekRjQs,6622
|
46
|
+
janito-1.3.0.dist-info/licenses/LICENSE,sha256=sHBqv0bvtrb29H7WRR-Z603YHm9pLtJIo3nHU_9cmgE,1091
|
47
|
+
janito-1.3.0.dist-info/METADATA,sha256=5qk7QmINq8vmDCcUMj00IuWrOSMEj_NPNeiroXKH_fo,9071
|
48
|
+
janito-1.3.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
49
|
+
janito-1.3.0.dist-info/entry_points.txt,sha256=wIo5zZxbmu4fC-ZMrsKD0T0vq7IqkOOLYhrqRGypkx4,48
|
50
|
+
janito-1.3.0.dist-info/top_level.txt,sha256=m0NaVCq0-ivxbazE2-ND0EA9Hmuijj_OGkmCbnBcCig,7
|
51
|
+
janito-1.3.0.dist-info/RECORD,,
|
janito/agent/tools/bash_exec.py
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
from janito.agent.tool_handler import ToolHandler
|
2
|
-
from janito.agent.tools.rich_utils import print_info, print_bash_stdout, print_bash_stderr
|
3
|
-
import subprocess
|
4
|
-
import threading
|
5
|
-
from typing import Callable, Optional
|
6
|
-
|
7
|
-
|
8
|
-
@ToolHandler.register_tool
|
9
|
-
def bash_exec(command: str, on_progress: Optional[Callable[[dict], None]] = None) -> str:
|
10
|
-
"""
|
11
|
-
command: The Bash command to execute.
|
12
|
-
on_progress: Optional callback function for streaming progress updates.
|
13
|
-
|
14
|
-
Execute a non interactive bash command and print output live.
|
15
|
-
|
16
|
-
Returns:
|
17
|
-
str: A formatted message string containing stdout, stderr, and return code.
|
18
|
-
"""
|
19
|
-
print_info(f"[bash_exec] Executing command: {command}")
|
20
|
-
result = {'stdout': '', 'stderr': '', 'returncode': None}
|
21
|
-
|
22
|
-
def run_command():
|
23
|
-
try:
|
24
|
-
process = subprocess.Popen(
|
25
|
-
command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8', errors='replace'
|
26
|
-
)
|
27
|
-
stdout_lines = []
|
28
|
-
stderr_lines = []
|
29
|
-
|
30
|
-
def read_stream(stream, collector, print_func, stream_name):
|
31
|
-
for line in iter(stream.readline, ''):
|
32
|
-
collector.append(line)
|
33
|
-
print_func(line.rstrip())
|
34
|
-
if callable(on_progress):
|
35
|
-
on_progress({'stream': stream_name, 'line': line.rstrip()})
|
36
|
-
stream.close()
|
37
|
-
|
38
|
-
stdout_thread = threading.Thread(target=read_stream, args=(process.stdout, stdout_lines, print_bash_stdout, 'stdout'))
|
39
|
-
stderr_thread = threading.Thread(target=read_stream, args=(process.stderr, stderr_lines, print_bash_stderr, 'stderr'))
|
40
|
-
stdout_thread.start()
|
41
|
-
stderr_thread.start()
|
42
|
-
stdout_thread.join()
|
43
|
-
stderr_thread.join()
|
44
|
-
result['returncode'] = process.wait()
|
45
|
-
result['stdout'] = ''.join(stdout_lines)
|
46
|
-
result['stderr'] = ''.join(stderr_lines)
|
47
|
-
except Exception as e:
|
48
|
-
result['stderr'] = str(e)
|
49
|
-
result['returncode'] = -1
|
50
|
-
|
51
|
-
thread = threading.Thread(target=run_command)
|
52
|
-
thread.start()
|
53
|
-
thread.join() # Wait for the thread to finish
|
54
|
-
|
55
|
-
print_info(f"[bash_exec] Command execution completed.")
|
56
|
-
print_info(f"[bash_exec] Return code: {result['returncode']}")
|
57
|
-
|
58
|
-
return f"stdout:\n{result['stdout']}\nstderr:\n{result['stderr']}\nreturncode: {result['returncode']}"
|
@@ -1,19 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
from janito.agent.tool_handler import ToolHandler
|
3
|
-
from janito.agent.tools.rich_utils import print_info, print_success, print_error, format_path
|
4
|
-
|
5
|
-
@ToolHandler.register_tool
|
6
|
-
def create_directory(path: str) -> str:
|
7
|
-
"""
|
8
|
-
Create a directory at the specified path.
|
9
|
-
|
10
|
-
path: The path of the directory to create
|
11
|
-
"""
|
12
|
-
print_info(f"📁 Creating directory: '{format_path(path)}' ... ")
|
13
|
-
try:
|
14
|
-
os.makedirs(path, exist_ok=True)
|
15
|
-
print_success("✅ Success")
|
16
|
-
return f"✅ Directory '{path}' created successfully."
|
17
|
-
except Exception as e:
|
18
|
-
print_error(f"❌ Error: {e}")
|
19
|
-
return f"❌ Error creating directory '{path}': {e}"
|
@@ -1,43 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
from janito.agent.tool_handler import ToolHandler
|
3
|
-
from janito.agent.tools.rich_utils import print_info, print_success, print_error, format_path
|
4
|
-
|
5
|
-
@ToolHandler.register_tool
|
6
|
-
def create_file(path: str, content: str, overwrite: bool = False) -> str:
|
7
|
-
"""
|
8
|
-
Create a file with the specified content.
|
9
|
-
|
10
|
-
path: The path of the file to create
|
11
|
-
content: The content to write into the file
|
12
|
-
overwrite: Whether to overwrite the file if it exists (default: False)
|
13
|
-
"""
|
14
|
-
old_lines = None
|
15
|
-
if os.path.exists(path):
|
16
|
-
if os.path.isdir(path):
|
17
|
-
print_error("❌ Error: is a directory")
|
18
|
-
return f"❌ Cannot create file: '{path}' is an existing directory."
|
19
|
-
if overwrite:
|
20
|
-
try:
|
21
|
-
with open(path, "r", encoding="utf-8") as f:
|
22
|
-
old_lines = sum(1 for _ in f)
|
23
|
-
except Exception:
|
24
|
-
old_lines = 'unknown'
|
25
|
-
else:
|
26
|
-
print_error(f"❗ Error: file '{path}' exists and overwrite is False")
|
27
|
-
return f"❗ Cannot create file: '{path}' already exists and overwrite is False."
|
28
|
-
|
29
|
-
new_lines = content.count('\n') + 1 if content else 0
|
30
|
-
|
31
|
-
if old_lines is not None:
|
32
|
-
print_info(f"♻️ Replacing file: '{format_path(path)}' (line count: {old_lines} -> {new_lines}) ... ")
|
33
|
-
else:
|
34
|
-
print_info(f"📝 Creating file: '{format_path(path)}' (lines: {new_lines}) ... ")
|
35
|
-
|
36
|
-
try:
|
37
|
-
with open(path, "w", encoding="utf-8") as f:
|
38
|
-
f.write(content)
|
39
|
-
print_success("✅ Success")
|
40
|
-
return f"✅ Successfully created the file at '{path}'."
|
41
|
-
except Exception as e:
|
42
|
-
print_error(f"❌ Error: {e}")
|
43
|
-
return f"❌ Failed to create the file at '{path}': {e}"
|
@@ -1,48 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
from janito.agent.tool_handler import ToolHandler
|
3
|
-
from janito.agent.tools.rich_utils import print_info, print_success, print_error, format_path
|
4
|
-
|
5
|
-
@ToolHandler.register_tool
|
6
|
-
def file_str_replace(path: str, old_string: str, new_string: str) -> str:
|
7
|
-
"""
|
8
|
-
Replace a unique occurrence of a string in a file.
|
9
|
-
|
10
|
-
path: Path to the file
|
11
|
-
old_string: The exact string to replace
|
12
|
-
- must be unique within all the file lines
|
13
|
-
new_string: The replacement string
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
Returns a message indicating success on an error
|
18
|
-
"""
|
19
|
-
if not os.path.isfile(path):
|
20
|
-
print_error(f"❌ Error: '{path}' is not a valid file.")
|
21
|
-
return f"❌ Error: '{path}' is not a valid file."
|
22
|
-
|
23
|
-
try:
|
24
|
-
with open(path, 'r', encoding='utf-8') as f:
|
25
|
-
content = f.read()
|
26
|
-
except Exception as e:
|
27
|
-
print_error(f"❌ Error reading file: {e}")
|
28
|
-
return f"❌ Failed to read file '{path}': {e}"
|
29
|
-
|
30
|
-
num_matches = content.count(old_string)
|
31
|
-
|
32
|
-
if num_matches == 0:
|
33
|
-
print_info(f"ℹ️ No occurrences of the target string found in '{format_path(path)}'.")
|
34
|
-
return f"ℹ️ No occurrences of the target string found in '{path}'."
|
35
|
-
elif num_matches > 1:
|
36
|
-
print_error(f"❌ Error: More than one occurrence ({num_matches}) of the target string found in '{format_path(path)}'. Aborting replacement.")
|
37
|
-
return f"❌ Error: More than one occurrence ({num_matches}) of the target string found in '{path}'. Aborting replacement."
|
38
|
-
|
39
|
-
new_content = content.replace(old_string, new_string, 1)
|
40
|
-
|
41
|
-
try:
|
42
|
-
with open(path, 'w', encoding='utf-8') as f:
|
43
|
-
f.write(new_content)
|
44
|
-
print_success(f"✅ Replaced the unique occurrence in '{format_path(path)}'.")
|
45
|
-
return f"✅ Successfully replaced the unique occurrence in '{path}'."
|
46
|
-
except Exception as e:
|
47
|
-
print_error(f"❌ Error writing file: {e}")
|
48
|
-
return f"❌ Failed to write updated content to '{path}': {e}"
|
janito/agent/tools/move_file.py
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
import shutil
|
2
|
-
import os
|
3
|
-
from janito.agent.tool_handler import ToolHandler
|
4
|
-
from janito.agent.tools.rich_utils import print_info, print_success, print_error, format_path
|
5
|
-
|
6
|
-
|
7
|
-
@ToolHandler.register_tool
|
8
|
-
def move_file(source_path: str, destination_path: str, overwrite: bool = False) -> str:
|
9
|
-
"""
|
10
|
-
Move a file or directory from source_path to destination_path.
|
11
|
-
|
12
|
-
source_path: The path of the file or directory to move
|
13
|
-
destination_path: The target path
|
14
|
-
overwrite: Whether to overwrite the destination if it exists (default: False)
|
15
|
-
"""
|
16
|
-
print_info(f"🚚 Moving '{format_path(source_path)}' to '{format_path(destination_path)}' ... ")
|
17
|
-
try:
|
18
|
-
if not os.path.exists(source_path):
|
19
|
-
print_error("❌ Error: source does not exist")
|
20
|
-
return f"❌ Source path '{source_path}' does not exist."
|
21
|
-
|
22
|
-
if os.path.exists(destination_path):
|
23
|
-
if not overwrite:
|
24
|
-
print_error("❌ Error: destination exists and overwrite is False")
|
25
|
-
return f"❌ Destination path '{destination_path}' already exists. Use overwrite=True to replace it."
|
26
|
-
# Remove destination if overwrite is True
|
27
|
-
if os.path.isdir(destination_path):
|
28
|
-
shutil.rmtree(destination_path)
|
29
|
-
else:
|
30
|
-
os.remove(destination_path)
|
31
|
-
|
32
|
-
shutil.move(source_path, destination_path)
|
33
|
-
print_success("✅ Success")
|
34
|
-
return f"✅ Successfully moved '{source_path}' to '{destination_path}'."
|
35
|
-
except Exception as e:
|
36
|
-
print_error(f"❌ Error: {e}")
|
37
|
-
return f"❌ Failed to move '{source_path}' to '{destination_path}': {e}"
|
@@ -1,19 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
from janito.agent.tool_handler import ToolHandler
|
3
|
-
from janito.agent.tools.rich_utils import print_info, print_success, print_error, format_path
|
4
|
-
|
5
|
-
@ToolHandler.register_tool
|
6
|
-
def remove_file(path: str) -> str:
|
7
|
-
"""
|
8
|
-
Remove a specified file.
|
9
|
-
|
10
|
-
path: The path of the file to remove
|
11
|
-
"""
|
12
|
-
print_info(f"🗑️ Removing file: '{format_path(path)}' ... ")
|
13
|
-
try:
|
14
|
-
os.remove(path)
|
15
|
-
print_success("✅ Success")
|
16
|
-
return f"✅ Successfully deleted the file at '{path}'."
|
17
|
-
except Exception as e:
|
18
|
-
print_error(f"❌ Error: {e}")
|
19
|
-
return f"❌ Failed to delete the file at '{path}': {e}"
|
@@ -1,41 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import re
|
3
|
-
import fnmatch
|
4
|
-
from janito.agent.tool_handler import ToolHandler
|
5
|
-
from janito.agent.tools.rich_utils import print_info, print_success, print_error, format_path, format_number
|
6
|
-
from janito.agent.tools.gitignore_utils import load_gitignore_patterns, filter_ignored
|
7
|
-
|
8
|
-
@ToolHandler.register_tool
|
9
|
-
def search_text(directory: str, file_pattern: str, text_pattern: str, case_sensitive: bool = False):
|
10
|
-
"""
|
11
|
-
directory: Root directory to search.
|
12
|
-
file_pattern: Glob pattern for filenames (e.g., '*.py').
|
13
|
-
text_pattern: Regex pattern to search within files.
|
14
|
-
case_sensitive: Whether the search is case sensitive.
|
15
|
-
|
16
|
-
Returns a string with matches, each in 'filepath:line_number:matched_line' format, separated by newlines.
|
17
|
-
"""
|
18
|
-
print_info(f"🔎 Searching for pattern '{text_pattern}' in files under '{format_path(directory)}' matching '{file_pattern}' ...")
|
19
|
-
flags = 0 if case_sensitive else re.IGNORECASE
|
20
|
-
regex = re.compile(text_pattern, flags)
|
21
|
-
results = []
|
22
|
-
ignore_patterns = load_gitignore_patterns()
|
23
|
-
|
24
|
-
try:
|
25
|
-
for root, dirs, files in os.walk(directory):
|
26
|
-
dirs, files = filter_ignored(root, dirs, files, ignore_patterns)
|
27
|
-
for filename in fnmatch.filter(files, file_pattern):
|
28
|
-
filepath = os.path.join(root, filename)
|
29
|
-
try:
|
30
|
-
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
|
31
|
-
for lineno, line in enumerate(f, start=1):
|
32
|
-
if regex.search(line):
|
33
|
-
results.append(f"{filepath}:{lineno}:{line.rstrip()}")
|
34
|
-
except Exception as e:
|
35
|
-
print_error(f"❌ Error reading file '{filepath}': {e}")
|
36
|
-
continue # Ignore unreadable files
|
37
|
-
print_success(f"✅ Found {format_number(len(results))} matches")
|
38
|
-
except Exception as e:
|
39
|
-
print_error(f"❌ Error during search: {e}")
|
40
|
-
|
41
|
-
return "\n".join(results)
|
janito/agent/tools/view_file.py
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
from janito.agent.tool_handler import ToolHandler
|
3
|
-
from janito.agent.tools.rich_utils import print_info, print_success, print_error, format_path, format_number
|
4
|
-
|
5
|
-
@ToolHandler.register_tool
|
6
|
-
def view_file(path: str, start_line: int = 1, end_line: int = None) -> str:
|
7
|
-
"""
|
8
|
-
View the contents of a file or list the contents of a directory.
|
9
|
-
|
10
|
-
path: The path of the file or directory to view
|
11
|
-
start_line: The starting line number (1-based, default: 1)
|
12
|
-
end_line: The ending line number (inclusive). If None, view until end of file.
|
13
|
-
"""
|
14
|
-
print_info(f"📂 View '{format_path(path)}' lines {format_number(start_line)} to {format_number(end_line) if end_line else 'end of file'}")
|
15
|
-
if os.path.isdir(path):
|
16
|
-
files = os.listdir(path)
|
17
|
-
print_success(f"✅ {format_number(len(files))} items")
|
18
|
-
return "\n".join(files)
|
19
|
-
else:
|
20
|
-
with open(path, "r", encoding="utf-8") as f:
|
21
|
-
lines = f.readlines()
|
22
|
-
|
23
|
-
total_lines = len(lines)
|
24
|
-
if end_line is None or end_line > total_lines:
|
25
|
-
end_line = total_lines
|
26
|
-
|
27
|
-
# Adjust for 0-based index
|
28
|
-
start_idx = max(start_line - 1, 0)
|
29
|
-
end_idx = end_line
|
30
|
-
|
31
|
-
selected_lines = lines[start_idx:end_idx]
|
32
|
-
content = '\n'.join(f"{i + start_line}: {line.rstrip()}" for i, line in enumerate(selected_lines))
|
33
|
-
print_success(f"✅ Returned lines {format_number(start_line)} to {format_number(end_line)} of {format_number(total_lines)}")
|
34
|
-
return content
|
@@ -1,38 +0,0 @@
|
|
1
|
-
You are a helpful {{ role }}.
|
2
|
-
|
3
|
-
You will start every response with a concise plan on how to gather additional information.
|
4
|
-
|
5
|
-
<context>
|
6
|
-
Always review `docs/structure.md` before conducting file-specific searches.
|
7
|
-
Unless specified otherwise, look for the files that match the questions context.
|
8
|
-
Explore files that might be relevant to the current task.
|
9
|
-
</context>
|
10
|
-
|
11
|
-
<analysis>
|
12
|
-
When analyzing issues, you might want to look into the git history for clues.
|
13
|
-
When using read_file specific a minimum of 100 lines.
|
14
|
-
</analysis>
|
15
|
-
|
16
|
-
<editing>
|
17
|
-
If in doubt during editing, use the `ask_user` function to get additional information; otherwise, proceed and inform the user of the decision made.
|
18
|
-
|
19
|
-
When you need to make changes to a file, consider the following:
|
20
|
-
|
21
|
-
- Use `file_str_replace` when you want to update or fix specific text fragments within a file without altering the rest of its content. It is preferred over full file replacement when:
|
22
|
-
- Only small, targeted changes are needed.
|
23
|
-
- You want to avoid the risk of accidentally overwriting unrelated content.
|
24
|
-
- The file is large, and rewriting the entire file would be inefficient.
|
25
|
-
- You want to preserve formatting, comments, or code structure outside the replaced text.
|
26
|
-
|
27
|
-
- When replacing files, review their current content before requesting the update.
|
28
|
-
|
29
|
-
- When reorganizing, moving files, or functions, search for references in other files that might need to be updated accordingly.
|
30
|
-
</editing>
|
31
|
-
|
32
|
-
<finishing>
|
33
|
-
After performing changes:
|
34
|
-
|
35
|
-
- Review the README content if there are user-exposed or public API changes.
|
36
|
-
- Use `git commit` to save the changes. Unless requested otherwise, review `git diff` when composing the commit message.
|
37
|
-
- Review `docs/structure.md` considering discovered, created, or modified files.
|
38
|
-
</finishing>
|
janito-1.2.0.dist-info/METADATA
DELETED
@@ -1,85 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.4
|
2
|
-
Name: janito
|
3
|
-
Version: 1.2.0
|
4
|
-
Summary: An agent framework with built-in tools.
|
5
|
-
Author-email: João Pinto <joao.pinto@gmail.com>
|
6
|
-
License: MIT
|
7
|
-
Project-URL: homepage, https://github.com/joaompinto/janito
|
8
|
-
Project-URL: repository, https://github.com/joaompinto/janito
|
9
|
-
Keywords: agent,framework,tools,automation
|
10
|
-
Classifier: Programming Language :: Python :: 3
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
12
|
-
Classifier: Operating System :: OS Independent
|
13
|
-
Requires-Python: >=3.8
|
14
|
-
Description-Content-Type: text/markdown
|
15
|
-
License-File: LICENSE
|
16
|
-
Requires-Dist: rich
|
17
|
-
Requires-Dist: openai
|
18
|
-
Requires-Dist: flask
|
19
|
-
Requires-Dist: pathspec
|
20
|
-
Requires-Dist: beautifulsoup4
|
21
|
-
Dynamic: license-file
|
22
|
-
|
23
|
-
# 🚀 Janito: Natural Language Code Editing Agent
|
24
|
-
|
25
|
-
## ⚡ Quick Start
|
26
|
-
|
27
|
-
Run a one-off prompt:
|
28
|
-
```bash
|
29
|
-
python -m janito "Refactor the data processing module to improve readability."
|
30
|
-
```
|
31
|
-
|
32
|
-
Or start the interactive chat shell:
|
33
|
-
```bash
|
34
|
-
python -m janito
|
35
|
-
```
|
36
|
-
|
37
|
-
Launch the web UI:
|
38
|
-
```bash
|
39
|
-
python -m janito.web
|
40
|
-
```
|
41
|
-
|
42
|
-
---
|
43
|
-
|
44
|
-
Janito is a command-line and web-based AI agent designed to **edit code and manage files** using natural language instructions.
|
45
|
-
|
46
|
-
---
|
47
|
-
|
48
|
-
## ✨ Key Features
|
49
|
-
- 📝 **Code Editing via Natural Language:** Modify, create, or delete code files simply by describing the changes.
|
50
|
-
- 📁 **File & Directory Management:** Navigate, create, move, or remove files and folders.
|
51
|
-
- 🧠 **Context-Aware:** Understands your project structure for precise edits.
|
52
|
-
- 💬 **Interactive User Prompts:** Asks for clarification when needed.
|
53
|
-
- 🧩 **Extensible Tooling:** Built-in tools for file operations, shell commands, and more.
|
54
|
-
- 🌐 **Web Interface (In Development):** Upcoming simple web UI for streaming responses and tool progress.
|
55
|
-
|
56
|
-
---
|
57
|
-
|
58
|
-
## 📦 Installation
|
59
|
-
|
60
|
-
### Requirements
|
61
|
-
- Python 3.8+
|
62
|
-
|
63
|
-
...
|
64
|
-
|
65
|
-
### Configurable Options
|
66
|
-
|
67
|
-
| Key | Description | How to set | Default |
|
68
|
-
|---------------------|---------------------------------------------------------------------------------------------|-----------------------------------------------------------------|--------------------------------------------|
|
69
|
-
| `api_key` | API key for OpenAI-compatible service | `--set-api-key`, config file | _None_ (required) |
|
70
|
-
| `model` | Model name to use | `--model` (session only), `--set-local-config model=...`, or `--set-global-config` | `openai/gpt-4.1` |
|
71
|
-
| `base_url` | API base URL (OpenAI-compatible endpoint) | `--set-local-config base_url=...` or `--set-global-config` | `https://openrouter.ai/api/v1` |
|
72
|
-
| `role` | Role description for system prompt | CLI `--role` or config | "software engineer" |
|
73
|
-
| `system_prompt` | Override the entire system prompt as a raw string. | CLI `--system-prompt` or config | _Template-generated prompt_ |
|
74
|
-
| `system_prompt_file`| Use a plain text file as the system prompt (no template rendering, takes precedence over `system_prompt`). | CLI `--system-file` | _None_ |
|
75
|
-
| `temperature` | Sampling temperature (float, e.g., 0.0 - 2.0) | CLI `--temperature` or config | 0.2 |
|
76
|
-
| `max_tokens` | Maximum tokens for model response | CLI `--max-tokens` or config | 200000 |
|
77
|
-
| `disable_tools` | Disable tool use (no tools passed to agent) | CLI `--disable-tools` | _False_ |
|
78
|
-
|
79
|
-
#### System Prompt Precedence
|
80
|
-
|
81
|
-
- If `--system-file` is provided, the file's content is used as the system prompt (no template rendering).
|
82
|
-
- Otherwise, if `--system-prompt` or the config value is set, that string is used.
|
83
|
-
- Otherwise, a default template is rendered using the current role.
|
84
|
-
|
85
|
-
...
|