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/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
- file_path = Path(path)
44
+ full_path = resolve_path(path)
45
+ file_path = Path(full_path)
17
46
 
18
47
  if not file_path.exists():
19
- return f"Error: File '{path}' does not exist."
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
- if not is_safe_path(path):
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
- if not is_safe_path(path):
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
- if not is_safe_path(path):
68
- return "❌ Invalid path."
69
- full_path = os.path.join(os.getcwd(), path)
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,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (82.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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,,