forgecodecli 0.1.0__py3-none-any.whl → 0.2.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.
- forgecodecli/agent.py +126 -55
- forgecodecli/cli.py +317 -138
- forgecodecli/git_tools.py +69 -0
- forgecodecli/path_resolver.py +24 -0
- forgecodecli/tools.py +123 -17
- forgecodecli-0.2.0.dist-info/METADATA +198 -0
- forgecodecli-0.2.0.dist-info/RECORD +13 -0
- {forgecodecli-0.1.0.dist-info → forgecodecli-0.2.0.dist-info}/WHEEL +1 -1
- forgecodecli-0.1.0.dist-info/METADATA +0 -127
- forgecodecli-0.1.0.dist-info/RECORD +0 -11
- {forgecodecli-0.1.0.dist-info → forgecodecli-0.2.0.dist-info}/entry_points.txt +0 -0
- {forgecodecli-0.1.0.dist-info → forgecodecli-0.2.0.dist-info}/top_level.txt +0 -0
forgecodecli/tools.py
CHANGED
|
@@ -1,7 +1,35 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
import os
|
|
3
|
+
import shutil
|
|
4
|
+
from forgecodecli.path_resolver import resolve_path
|
|
3
5
|
|
|
6
|
+
|
|
7
|
+
_undo_stack = []
|
|
8
|
+
|
|
9
|
+
class FileOperation:
|
|
10
|
+
"""Represent a reversible file operation"""
|
|
11
|
+
def __init__(self, op_type: str, src: str, dst: str = None, content: str = None):
|
|
12
|
+
self.op_type = op_type # 'write', 'delete', 'move', 'mkdir', 'rmdir'
|
|
13
|
+
self.src = src
|
|
14
|
+
self.dst = dst
|
|
15
|
+
self.content = content # For write operations
|
|
16
|
+
def undo(self):
|
|
17
|
+
"""Reverse the operation"""
|
|
18
|
+
if self.op_type == "write":
|
|
19
|
+
os.remove(self.src)
|
|
20
|
+
elif self.op_type == "delete":
|
|
21
|
+
with open(self.src, "w", encoding="utf-8") as f:
|
|
22
|
+
f.write(self.content)
|
|
23
|
+
elif self.op_type == "move":
|
|
24
|
+
os.rename(self.dst, self.src)
|
|
25
|
+
elif self.op_type == "mkdir":
|
|
26
|
+
os.rmdir(self.src)
|
|
27
|
+
elif self.op_type == "rmdir":
|
|
28
|
+
os.makedirs(self.src, exist_ok=True)
|
|
29
|
+
|
|
30
|
+
|
|
4
31
|
def is_safe_path(path: str) -> bool:
|
|
32
|
+
"""Check if path is safe (no absolute paths, no parent directory traversal)"""
|
|
5
33
|
if not path:
|
|
6
34
|
return False
|
|
7
35
|
if os.path.isabs(path):
|
|
@@ -13,19 +41,17 @@ def is_safe_path(path: str) -> bool:
|
|
|
13
41
|
|
|
14
42
|
def read_file(path: str) -> str:
|
|
15
43
|
"""Read a file and return its contents"""
|
|
16
|
-
|
|
44
|
+
full_path = resolve_path(path)
|
|
45
|
+
file_path = Path(full_path)
|
|
17
46
|
|
|
18
47
|
if not file_path.exists():
|
|
19
|
-
|
|
48
|
+
return f"Error: File '{path}' does not exist."
|
|
20
49
|
|
|
21
50
|
return file_path.read_text()
|
|
22
51
|
|
|
23
52
|
|
|
24
|
-
def list_files(path:str = ".")-> str:
|
|
25
|
-
|
|
26
|
-
return "Error: Unsafe path detected."
|
|
27
|
-
|
|
28
|
-
full_path = os.path.join(os.getcwd(), path)
|
|
53
|
+
def list_files(path: str = ".") -> str:
|
|
54
|
+
full_path = resolve_path(path)
|
|
29
55
|
|
|
30
56
|
if not os.path.exists(full_path):
|
|
31
57
|
return "❌ Path does not exist."
|
|
@@ -41,12 +67,10 @@ def list_files(path:str = ".")-> str:
|
|
|
41
67
|
return "\n".join(sorted(items))
|
|
42
68
|
except Exception as e:
|
|
43
69
|
return f"❌ Error reading directory: {str(e)}"
|
|
70
|
+
|
|
44
71
|
|
|
45
72
|
def write_file(path: str, content: str) -> str:
|
|
46
|
-
|
|
47
|
-
return "❌ Invalid path."
|
|
48
|
-
|
|
49
|
-
full_path = os.path.join(os.getcwd(), path)
|
|
73
|
+
full_path = resolve_path(path)
|
|
50
74
|
|
|
51
75
|
# Prevent overwriting existing files (for now)
|
|
52
76
|
if os.path.exists(full_path):
|
|
@@ -59,16 +83,98 @@ def write_file(path: str, content: str) -> str:
|
|
|
59
83
|
try:
|
|
60
84
|
with open(full_path, "w", encoding="utf-8") as f:
|
|
61
85
|
f.write(content)
|
|
86
|
+
_undo_stack.append(FileOperation("write", full_path))
|
|
87
|
+
|
|
62
88
|
return f"✅ File written: {path}"
|
|
63
89
|
except Exception:
|
|
64
90
|
return "❌ Failed to write file."
|
|
65
91
|
|
|
66
|
-
def create_dir(path: str)-> str:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
92
|
+
def create_dir(path: str) -> str:
|
|
93
|
+
full_path = resolve_path(path)
|
|
94
|
+
if os.path.exists(full_path):
|
|
95
|
+
return f"❌ Directory already exists: {path}"
|
|
70
96
|
try:
|
|
71
97
|
os.makedirs(full_path, exist_ok=True)
|
|
98
|
+
_undo_stack.append(FileOperation("mkdir", full_path))
|
|
72
99
|
return f"✅ Directory created: {path}"
|
|
73
|
-
except Exception:
|
|
74
|
-
return "❌ Failed to create directory
|
|
100
|
+
except Exception as e:
|
|
101
|
+
return f"❌ Failed to create directory: {str(e)}"
|
|
102
|
+
|
|
103
|
+
def delete_file(path: str) -> str:
|
|
104
|
+
full_path = resolve_path(path)
|
|
105
|
+
if not os.path.exists(full_path):
|
|
106
|
+
return f"❌ File '{path}' does not exist."
|
|
107
|
+
if os.path.isdir(full_path):
|
|
108
|
+
return f"❌ '{path}' is a directory, not a file."
|
|
109
|
+
try:
|
|
110
|
+
content = Path(full_path).read_text()
|
|
111
|
+
os.remove(full_path)
|
|
112
|
+
_undo_stack.append(FileOperation("delete", full_path, content=content))
|
|
113
|
+
return f"✅ File deleted: {path}"
|
|
114
|
+
except Exception as e:
|
|
115
|
+
return f"❌ Failed to delete file: {str(e)}"
|
|
116
|
+
|
|
117
|
+
def delete_dir(path: str) -> str:
|
|
118
|
+
full_path = resolve_path(path)
|
|
119
|
+
if not os.path.exists(full_path):
|
|
120
|
+
return f"❌ Directory '{path}' does not exist."
|
|
121
|
+
if not os.path.isdir(full_path):
|
|
122
|
+
return f"❌ '{path}' is not a directory."
|
|
123
|
+
try:
|
|
124
|
+
shutil.rmtree(full_path)
|
|
125
|
+
_undo_stack.append(FileOperation("rmdir", full_path))
|
|
126
|
+
return f"✅ Directory deleted: {path}"
|
|
127
|
+
except Exception as e:
|
|
128
|
+
return f"❌ Failed to delete directory: {str(e)}"
|
|
129
|
+
|
|
130
|
+
def move_file(src: str, dst: str) -> str:
|
|
131
|
+
full_src = resolve_path(src)
|
|
132
|
+
full_dst = resolve_path(dst)
|
|
133
|
+
|
|
134
|
+
if not os.path.exists(full_src):
|
|
135
|
+
return f"❌ Source file '{src}' does not exist."
|
|
136
|
+
if os.path.isdir(full_src):
|
|
137
|
+
return f"❌ Source '{src}' is a directory, not a file."
|
|
138
|
+
|
|
139
|
+
dst_parent = os.path.dirname(full_dst)
|
|
140
|
+
if dst_parent and not os.path.exists(dst_parent):
|
|
141
|
+
return f"❌ Destination parent directory does not exist."
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
os.rename(full_src, full_dst)
|
|
145
|
+
_undo_stack.append(FileOperation("move", full_src, full_dst))
|
|
146
|
+
return f"✅ File moved from '{src}' to '{dst}'"
|
|
147
|
+
except Exception as e:
|
|
148
|
+
return f"❌ Failed to move file: {str(e)}"
|
|
149
|
+
|
|
150
|
+
def move_dir(src: str, dst: str) -> str:
|
|
151
|
+
full_src = resolve_path(src)
|
|
152
|
+
full_dst = resolve_path(dst)
|
|
153
|
+
|
|
154
|
+
if not os.path.exists(full_src):
|
|
155
|
+
return f"❌ Source directory '{src}' does not exist."
|
|
156
|
+
if not os.path.isdir(full_src):
|
|
157
|
+
return f"❌ Source '{src}' is not a directory."
|
|
158
|
+
|
|
159
|
+
dst_parent = os.path.dirname(full_dst)
|
|
160
|
+
if dst_parent and not os.path.exists(dst_parent):
|
|
161
|
+
return f"❌ Destination parent directory does not exist."
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
os.rename(full_src, full_dst)
|
|
165
|
+
_undo_stack.append(FileOperation("move", full_src, full_dst))
|
|
166
|
+
return f"✅ Directory moved from '{src}' to '{dst}'"
|
|
167
|
+
except Exception as e:
|
|
168
|
+
return f"❌ Failed to move directory: {str(e)}"
|
|
169
|
+
|
|
170
|
+
def undo() -> str:
|
|
171
|
+
"""Undo the last file operation"""
|
|
172
|
+
if not _undo_stack:
|
|
173
|
+
return "⚠️ Nothing to undo."
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
operation = _undo_stack.pop()
|
|
177
|
+
operation.undo()
|
|
178
|
+
return f"✅ Undid {operation.op_type} operation"
|
|
179
|
+
except Exception as e:
|
|
180
|
+
return f"❌ Failed to undo: {str(e)}"
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: forgecodecli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A minimal agentic CLI for forging code
|
|
5
|
+
Author: Sudhanshu
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: typer[all]>=0.9.0
|
|
10
|
+
Requires-Dist: openai>=1.0.0
|
|
11
|
+
Requires-Dist: keyring>=24.0.0
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
14
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
15
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
16
|
+
Provides-Extra: anthropic
|
|
17
|
+
Requires-Dist: anthropic>=0.25.0; extra == "anthropic"
|
|
18
|
+
|
|
19
|
+
# ForgeCodeCLI
|
|
20
|
+
|
|
21
|
+
An agentic, file-aware command-line tool that lets you manage and modify your codebase using natural language — powered by LLMs.
|
|
22
|
+
|
|
23
|
+
It acts as a safe, deterministic AI agent that can read files, create/delete directories, and write code only through explicit tools, not raw hallucination.
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- ✅ **Agentic workflow** - LLM decides actions, CLI executes them safely
|
|
28
|
+
- ✅ **File operations** - Read, list, create, write, delete, move files & directories
|
|
29
|
+
- ✅ **Undo support** - Reverse the last file operation with `undo`
|
|
30
|
+
- ✅ **Multi-provider LLMs** - Gemini, OpenAI (GPT), Anthropic (Claude), Groq
|
|
31
|
+
- ✅ **Model selection** - Choose specific models for each provider
|
|
32
|
+
- ✅ **Secure storage** - API keys stored in system keyring (no env vars)
|
|
33
|
+
- ✅ **Deterministic** - Rule-based execution with validation
|
|
34
|
+
- ✅ **Interactive CLI** - Real-time agent feedback
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
Requires Python 3.9+
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install forgecodecli
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Optional:** For Anthropic (Claude) support, install with the anthropic extra:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install forgecodecli[anthropic]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or install later when prompted during setup.
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
### Initialize (one-time setup)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
forgecodecli init
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
You will be prompted to:
|
|
61
|
+
|
|
62
|
+
1. **Select LLM Provider**
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
1) Google Gemini
|
|
66
|
+
2) OpenAI
|
|
67
|
+
3) Anthropic (Claude)
|
|
68
|
+
4) Groq
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
2. **Select Model** (varies by provider)
|
|
72
|
+
- Gemini: `gemini-2.5-flash`, `gemini-2.0-flash`, `gemini-1.5-pro`
|
|
73
|
+
- OpenAI: `gpt-4o`, `gpt-4-turbo`, `gpt-3.5-turbo`
|
|
74
|
+
- Claude: `claude-3-5-sonnet`, `claude-3-opus`, `claude-3-haiku`
|
|
75
|
+
- Groq: `llama-3.3-70b`, `mixtral-8x7b`, `gemma2-9b-it`
|
|
76
|
+
|
|
77
|
+
3. **Enter API Key** (stored securely in system keyring)
|
|
78
|
+
|
|
79
|
+
### Start the agent
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
forgecodecli
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
You are now in interactive agent mode. Example commands:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
create a folder src/app with main.py that prints "Hello World"
|
|
89
|
+
read the config.py file
|
|
90
|
+
list all files in src
|
|
91
|
+
delete old_backup folder
|
|
92
|
+
move test.py to tests/test.py
|
|
93
|
+
undo
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Type `help` for built-in commands, or press `Ctrl + C` to exit.
|
|
97
|
+
|
|
98
|
+
## Reset Configuration
|
|
99
|
+
|
|
100
|
+
To remove all configuration and API keys:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
forgecodecli reset
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Security
|
|
107
|
+
|
|
108
|
+
- API keys are stored using the system keyring
|
|
109
|
+
- No API keys are written to config files or environment variables
|
|
110
|
+
- Config files contain only non-sensitive metadata
|
|
111
|
+
|
|
112
|
+
## How It Works
|
|
113
|
+
|
|
114
|
+
1. You enter a natural language command
|
|
115
|
+
2. The LLM decides the next valid action
|
|
116
|
+
3. ForgeCodeCLI executes the action with validation
|
|
117
|
+
4. The agent receives feedback and responds
|
|
118
|
+
5. Process repeats until agent provides an answer
|
|
119
|
+
|
|
120
|
+
**Safety mechanisms:**
|
|
121
|
+
|
|
122
|
+
- Action limit of 2 per request (prevents infinite loops)
|
|
123
|
+
- Conversation context maintained for agent awareness
|
|
124
|
+
- All operations logged and reversible with `undo`
|
|
125
|
+
- Strict tool validation
|
|
126
|
+
|
|
127
|
+
## Supported Actions
|
|
128
|
+
|
|
129
|
+
The agent can execute these operations:
|
|
130
|
+
|
|
131
|
+
**File Operations:**
|
|
132
|
+
| Action | Description |
|
|
133
|
+
|--------|-------------|
|
|
134
|
+
| `read_file` | Read and display file contents |
|
|
135
|
+
| `list_files` | List files in a directory |
|
|
136
|
+
| `create_dir` | Create new directories |
|
|
137
|
+
| `write_file` | Create and write files |
|
|
138
|
+
| `delete_file` | Delete files permanently |
|
|
139
|
+
| `delete_dir` | Delete directories |
|
|
140
|
+
| `move_file` | Move or rename files |
|
|
141
|
+
| `move_dir` | Move or rename directories |
|
|
142
|
+
| `undo` | Reverse the last operation |
|
|
143
|
+
|
|
144
|
+
**Git Operations:**
|
|
145
|
+
| Action | Description |
|
|
146
|
+
|--------|-------------|
|
|
147
|
+
| `git_init` | Initialize git repository |
|
|
148
|
+
| `git_add` | Stage files for commit |
|
|
149
|
+
| `git_commit` | Commit staged changes |
|
|
150
|
+
| `git_push` | Push to remote repository |
|
|
151
|
+
| `git_set_origin` | Set remote repository URL |
|
|
152
|
+
| `git_status` | Show repository status |
|
|
153
|
+
| `git_log` | View commit history |
|
|
154
|
+
| `git_branch` | Manage branches |
|
|
155
|
+
| `git_pull` | Pull from remote |
|
|
156
|
+
| `git_clone` | Clone a repository |
|
|
157
|
+
|
|
158
|
+
All actions are executed safely with validation and error handling.
|
|
159
|
+
|
|
160
|
+
## Roadmap
|
|
161
|
+
|
|
162
|
+
### ✅ v1 (Released)
|
|
163
|
+
|
|
164
|
+
- Basic file operations (read, list, create, write)
|
|
165
|
+
- Gemini support
|
|
166
|
+
- Interactive CLI
|
|
167
|
+
|
|
168
|
+
### ✅ v2 (Released)
|
|
169
|
+
|
|
170
|
+
- **Undo functionality** - Stack-based operation reversal
|
|
171
|
+
- **Delete & move operations** - Full file/directory manipulation
|
|
172
|
+
- **Multi-provider support** - Gemini, OpenAI, Anthropic, Groq
|
|
173
|
+
- **Model selection** - Choose specific models per provider
|
|
174
|
+
- **Auto-install SDKs** - Anthropic SDK installs on demand
|
|
175
|
+
- **Fixed agent loop** - Proper conversation flow with max actions
|
|
176
|
+
|
|
177
|
+
### ✅ v3 (Current)
|
|
178
|
+
|
|
179
|
+
- **Git operations** - init, add, commit, push, pull, status, log, branch, clone, set_origin
|
|
180
|
+
- **Git workflow support** - Full version control integration
|
|
181
|
+
- **Repository management** - Clone and manage git repos
|
|
182
|
+
|
|
183
|
+
### 🚀 v4 (Planned)
|
|
184
|
+
|
|
185
|
+
- Copy files/directories
|
|
186
|
+
- Full undo/redo history (not just last operation)
|
|
187
|
+
- File search capabilities
|
|
188
|
+
- Code generation templates
|
|
189
|
+
- Batch operations
|
|
190
|
+
- Backup/snapshot functionality
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
MIT License
|
|
195
|
+
|
|
196
|
+
## Author
|
|
197
|
+
|
|
198
|
+
Built by Sudhanshu
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
forgecodecli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
forgecodecli/agent.py,sha256=4CDG8mVWwCpqmJlxrqIa7JXB3TQV-w_BBbXROpv9YPY,9651
|
|
3
|
+
forgecodecli/cli.py,sha256=K7YE7gAoOjSSQkoXE5L5gkHnmDP-kdedDtQLeo3yitw,15214
|
|
4
|
+
forgecodecli/config.py,sha256=VFl21RTu0aTkYS3ConcgLoHD7LuV890d9LoXsAaYs5M,595
|
|
5
|
+
forgecodecli/git_tools.py,sha256=BAjkqLlv-TqLhJka9UM2wHxXJAgVOjKLmgZgJp83thQ,2168
|
|
6
|
+
forgecodecli/path_resolver.py,sha256=sjAATxw6Y6pfDri1LIHXTm88iVkngNpiYf_sYSJArno,509
|
|
7
|
+
forgecodecli/secrets.py,sha256=ZEaHYF9TkibST0IwEBHZbbazaLWJZR04n4FyGx9n1gU,328
|
|
8
|
+
forgecodecli/tools.py,sha256=3NzX7S7eHVm0jVK4O1Z8BstIlZPvSVsRK5bbCOu02jE,6094
|
|
9
|
+
forgecodecli-0.2.0.dist-info/METADATA,sha256=yWBa7ZGhVvZwOpmxwTVnQ0ieZc7E1cykYtv7NGf65eM,5595
|
|
10
|
+
forgecodecli-0.2.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
11
|
+
forgecodecli-0.2.0.dist-info/entry_points.txt,sha256=KQDckJoVfVJtVxu3icUI99k-gpbIn_tihUG_ZwU4BAw,55
|
|
12
|
+
forgecodecli-0.2.0.dist-info/top_level.txt,sha256=OJLRIfDbRvaNKg55kSvPDagGlTxajt3m1jnI3X5x204,13
|
|
13
|
+
forgecodecli-0.2.0.dist-info/RECORD,,
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: forgecodecli
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: A minimal agentic CLI for forging code
|
|
5
|
-
Author: Sudhanshu
|
|
6
|
-
License: MIT
|
|
7
|
-
Requires-Python: >=3.9
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
Requires-Dist: typer[all]>=0.9.0
|
|
10
|
-
Requires-Dist: openai>=1.0.0
|
|
11
|
-
Requires-Dist: keyring>=24.0.0
|
|
12
|
-
Provides-Extra: dev
|
|
13
|
-
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
14
|
-
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
15
|
-
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
16
|
-
|
|
17
|
-
# ForgeCodeCLI
|
|
18
|
-
|
|
19
|
-
An agentic, file-aware command-line tool that lets you manage and modify your codebase using natural language — powered by LLMs.
|
|
20
|
-
|
|
21
|
-
It acts as a safe, deterministic AI agent that can read files, create directories, and write code only through explicit tools, not raw hallucination.
|
|
22
|
-
|
|
23
|
-
## Features
|
|
24
|
-
|
|
25
|
-
- Agentic workflow (LLM decides actions, CLI executes them)
|
|
26
|
-
- File-aware (read, list, create, write files & directories)
|
|
27
|
-
- Secure API key storage (no env vars required after setup)
|
|
28
|
-
- Deterministic and rule-based execution
|
|
29
|
-
- Interactive CLI experience
|
|
30
|
-
- Built to support multiple LLM providers (Gemini first)
|
|
31
|
-
|
|
32
|
-
## Installation
|
|
33
|
-
|
|
34
|
-
Requires Python 3.9+
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
pip install forgecodecli
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Quick Start
|
|
41
|
-
|
|
42
|
-
### Initialize (one-time setup)
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
forgecodecli init
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
You will be prompted to:
|
|
49
|
-
- Select an LLM provider
|
|
50
|
-
- Enter your API key (stored securely)
|
|
51
|
-
|
|
52
|
-
### Start the agent
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
forgecodecli
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
You are now in interactive agent mode. Example commands:
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
create a folder src/app and add a main.py file that prints hello
|
|
62
|
-
read the README.md file
|
|
63
|
-
list all files in the src directory
|
|
64
|
-
quit
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Or press `Ctrl + C` to exit.
|
|
68
|
-
|
|
69
|
-
## Reset Configuration
|
|
70
|
-
|
|
71
|
-
To remove all configuration and API keys:
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
forgecodecli reset
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
## Security
|
|
78
|
-
|
|
79
|
-
- API keys are stored using the system keyring
|
|
80
|
-
- No API keys are written to config files or environment variables
|
|
81
|
-
- Config files contain only non-sensitive metadata
|
|
82
|
-
|
|
83
|
-
## How It Works
|
|
84
|
-
|
|
85
|
-
1. You enter a natural language command
|
|
86
|
-
2. The LLM decides the next valid action
|
|
87
|
-
3. ForgeCodeCLI executes the action safely
|
|
88
|
-
4. The agent responds with the result
|
|
89
|
-
|
|
90
|
-
The agent is strictly limited to predefined tools, ensuring predictable and safe behavior.
|
|
91
|
-
|
|
92
|
-
## Supported Actions
|
|
93
|
-
|
|
94
|
-
- `read_file`
|
|
95
|
-
- `list_files`
|
|
96
|
-
- `create_dir`
|
|
97
|
-
- `write_file`
|
|
98
|
-
|
|
99
|
-
No action outside these tools is permitted.
|
|
100
|
-
|
|
101
|
-
## Status
|
|
102
|
-
|
|
103
|
-
This project is in active development.
|
|
104
|
-
|
|
105
|
-
**Current version supports:**
|
|
106
|
-
- Gemini LLM
|
|
107
|
-
- Interactive agent mode
|
|
108
|
-
|
|
109
|
-
**Planned features:**
|
|
110
|
-
- Multiple LLM providers
|
|
111
|
-
- Model switching
|
|
112
|
-
- Streaming responses
|
|
113
|
-
- Session memory
|
|
114
|
-
|
|
115
|
-
## License
|
|
116
|
-
|
|
117
|
-
MIT License
|
|
118
|
-
|
|
119
|
-
## Author
|
|
120
|
-
|
|
121
|
-
Built by Sudhanshu
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
forgecodecli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
forgecodecli/agent.py,sha256=ta-Ab8ORFzEQ0pbnCjf7CR6g4itBzZtjD6XMmay5Vgg,4124
|
|
3
|
-
forgecodecli/cli.py,sha256=h1ip6kRoDLwco5558vYfyCEp8XMQDNvTVt8cqBRhuJA,7116
|
|
4
|
-
forgecodecli/config.py,sha256=VFl21RTu0aTkYS3ConcgLoHD7LuV890d9LoXsAaYs5M,595
|
|
5
|
-
forgecodecli/secrets.py,sha256=ZEaHYF9TkibST0IwEBHZbbazaLWJZR04n4FyGx9n1gU,328
|
|
6
|
-
forgecodecli/tools.py,sha256=f6_bsblRpbYerHussznXNdnF93j3G6ppYWCHUgveoAI,2096
|
|
7
|
-
forgecodecli-0.1.0.dist-info/METADATA,sha256=4w3PTAFjS7q-n8FeJ16PoMutA_-YxMqZ2WyIAlOpKAE,2677
|
|
8
|
-
forgecodecli-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
-
forgecodecli-0.1.0.dist-info/entry_points.txt,sha256=KQDckJoVfVJtVxu3icUI99k-gpbIn_tihUG_ZwU4BAw,55
|
|
10
|
-
forgecodecli-0.1.0.dist-info/top_level.txt,sha256=OJLRIfDbRvaNKg55kSvPDagGlTxajt3m1jnI3X5x204,13
|
|
11
|
-
forgecodecli-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|