forgecodecli 0.1.0__tar.gz → 0.2.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.
@@ -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,180 @@
1
+ # ForgeCodeCLI
2
+
3
+ An agentic, file-aware command-line tool that lets you manage and modify your codebase using natural language — powered by LLMs.
4
+
5
+ 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.
6
+
7
+ ## Features
8
+
9
+ - ✅ **Agentic workflow** - LLM decides actions, CLI executes them safely
10
+ - ✅ **File operations** - Read, list, create, write, delete, move files & directories
11
+ - ✅ **Undo support** - Reverse the last file operation with `undo`
12
+ - ✅ **Multi-provider LLMs** - Gemini, OpenAI (GPT), Anthropic (Claude), Groq
13
+ - ✅ **Model selection** - Choose specific models for each provider
14
+ - ✅ **Secure storage** - API keys stored in system keyring (no env vars)
15
+ - ✅ **Deterministic** - Rule-based execution with validation
16
+ - ✅ **Interactive CLI** - Real-time agent feedback
17
+
18
+ ## Installation
19
+
20
+ Requires Python 3.9+
21
+
22
+ ```bash
23
+ pip install forgecodecli
24
+ ```
25
+
26
+ **Optional:** For Anthropic (Claude) support, install with the anthropic extra:
27
+
28
+ ```bash
29
+ pip install forgecodecli[anthropic]
30
+ ```
31
+
32
+ Or install later when prompted during setup.
33
+
34
+ ## Quick Start
35
+
36
+ ### Initialize (one-time setup)
37
+
38
+ ```bash
39
+ forgecodecli init
40
+ ```
41
+
42
+ You will be prompted to:
43
+
44
+ 1. **Select LLM Provider**
45
+
46
+ ```
47
+ 1) Google Gemini
48
+ 2) OpenAI
49
+ 3) Anthropic (Claude)
50
+ 4) Groq
51
+ ```
52
+
53
+ 2. **Select Model** (varies by provider)
54
+ - Gemini: `gemini-2.5-flash`, `gemini-2.0-flash`, `gemini-1.5-pro`
55
+ - OpenAI: `gpt-4o`, `gpt-4-turbo`, `gpt-3.5-turbo`
56
+ - Claude: `claude-3-5-sonnet`, `claude-3-opus`, `claude-3-haiku`
57
+ - Groq: `llama-3.3-70b`, `mixtral-8x7b`, `gemma2-9b-it`
58
+
59
+ 3. **Enter API Key** (stored securely in system keyring)
60
+
61
+ ### Start the agent
62
+
63
+ ```bash
64
+ forgecodecli
65
+ ```
66
+
67
+ You are now in interactive agent mode. Example commands:
68
+
69
+ ```
70
+ create a folder src/app with main.py that prints "Hello World"
71
+ read the config.py file
72
+ list all files in src
73
+ delete old_backup folder
74
+ move test.py to tests/test.py
75
+ undo
76
+ ```
77
+
78
+ Type `help` for built-in commands, or press `Ctrl + C` to exit.
79
+
80
+ ## Reset Configuration
81
+
82
+ To remove all configuration and API keys:
83
+
84
+ ```bash
85
+ forgecodecli reset
86
+ ```
87
+
88
+ ## Security
89
+
90
+ - API keys are stored using the system keyring
91
+ - No API keys are written to config files or environment variables
92
+ - Config files contain only non-sensitive metadata
93
+
94
+ ## How It Works
95
+
96
+ 1. You enter a natural language command
97
+ 2. The LLM decides the next valid action
98
+ 3. ForgeCodeCLI executes the action with validation
99
+ 4. The agent receives feedback and responds
100
+ 5. Process repeats until agent provides an answer
101
+
102
+ **Safety mechanisms:**
103
+
104
+ - Action limit of 2 per request (prevents infinite loops)
105
+ - Conversation context maintained for agent awareness
106
+ - All operations logged and reversible with `undo`
107
+ - Strict tool validation
108
+
109
+ ## Supported Actions
110
+
111
+ The agent can execute these operations:
112
+
113
+ **File Operations:**
114
+ | Action | Description |
115
+ |--------|-------------|
116
+ | `read_file` | Read and display file contents |
117
+ | `list_files` | List files in a directory |
118
+ | `create_dir` | Create new directories |
119
+ | `write_file` | Create and write files |
120
+ | `delete_file` | Delete files permanently |
121
+ | `delete_dir` | Delete directories |
122
+ | `move_file` | Move or rename files |
123
+ | `move_dir` | Move or rename directories |
124
+ | `undo` | Reverse the last operation |
125
+
126
+ **Git Operations:**
127
+ | Action | Description |
128
+ |--------|-------------|
129
+ | `git_init` | Initialize git repository |
130
+ | `git_add` | Stage files for commit |
131
+ | `git_commit` | Commit staged changes |
132
+ | `git_push` | Push to remote repository |
133
+ | `git_set_origin` | Set remote repository URL |
134
+ | `git_status` | Show repository status |
135
+ | `git_log` | View commit history |
136
+ | `git_branch` | Manage branches |
137
+ | `git_pull` | Pull from remote |
138
+ | `git_clone` | Clone a repository |
139
+
140
+ All actions are executed safely with validation and error handling.
141
+
142
+ ## Roadmap
143
+
144
+ ### ✅ v1 (Released)
145
+
146
+ - Basic file operations (read, list, create, write)
147
+ - Gemini support
148
+ - Interactive CLI
149
+
150
+ ### ✅ v2 (Released)
151
+
152
+ - **Undo functionality** - Stack-based operation reversal
153
+ - **Delete & move operations** - Full file/directory manipulation
154
+ - **Multi-provider support** - Gemini, OpenAI, Anthropic, Groq
155
+ - **Model selection** - Choose specific models per provider
156
+ - **Auto-install SDKs** - Anthropic SDK installs on demand
157
+ - **Fixed agent loop** - Proper conversation flow with max actions
158
+
159
+ ### ✅ v3 (Current)
160
+
161
+ - **Git operations** - init, add, commit, push, pull, status, log, branch, clone, set_origin
162
+ - **Git workflow support** - Full version control integration
163
+ - **Repository management** - Clone and manage git repos
164
+
165
+ ### 🚀 v4 (Planned)
166
+
167
+ - Copy files/directories
168
+ - Full undo/redo history (not just last operation)
169
+ - File search capabilities
170
+ - Code generation templates
171
+ - Batch operations
172
+ - Backup/snapshot functionality
173
+
174
+ ## License
175
+
176
+ MIT License
177
+
178
+ ## Author
179
+
180
+ Built by Sudhanshu
@@ -0,0 +1,215 @@
1
+ # from dotenv import load_dotenv
2
+ from openai import OpenAI
3
+ from openai import RateLimitError
4
+ import json
5
+ from json import JSONDecoder
6
+ from forgecodecli.config import load_config
7
+ from forgecodecli.secrets import load_api_key
8
+ from forgecodecli.tools import undo
9
+ from forgecodecli.git_tools import (
10
+ git_init, git_add, git_commit, git_push, git_set_origin,
11
+ git_status, git_log, git_branch, git_pull, git_clone
12
+ )
13
+
14
+ # Provider base URLs (OpenAI-compatible)
15
+ PROVIDER_BASE_URLS = {
16
+ "gemini": "https://generativelanguage.googleapis.com/v1beta/openai/",
17
+ "openai": None, # default OpenAI
18
+ "groq": "https://api.groq.com/openai/v1",
19
+ }
20
+
21
+ def get_client():
22
+ config = load_config()
23
+ api_key = load_api_key()
24
+
25
+ if not config:
26
+ raise RuntimeError(
27
+ "ForgeCodeCLI is not set up. Run `forgecodecli init`."
28
+ )
29
+
30
+ if not api_key:
31
+ raise RuntimeError(
32
+ "API key not found. Run `forgecodecli init` again."
33
+ )
34
+
35
+ provider = config.get("provider", "gemini")
36
+
37
+ if provider == "anthropic":
38
+ try:
39
+ import anthropic as _anthropic
40
+ return _anthropic.Anthropic(api_key=api_key)
41
+ except ImportError:
42
+ raise RuntimeError(
43
+ "Anthropic package not installed. Run: pip install anthropic"
44
+ )
45
+
46
+ base_url = PROVIDER_BASE_URLS.get(provider)
47
+ if base_url:
48
+ return OpenAI(api_key=api_key, base_url=base_url)
49
+ return OpenAI(api_key=api_key)
50
+
51
+ SYSTEM_PROMPT = """
52
+ You are a file and git management agent that executes file and version control operations. You MUST use the actions below to fulfill user requests. You respond ONLY with valid JSON.
53
+
54
+ ═══════════════════════════════════════════════════════════════
55
+ ABSOLUTE RULES - VIOLATIONS ARE FORBIDDEN
56
+ ═══════════════════════════════════════════════════════════════
57
+
58
+ - You CAN delete files. You CAN delete directories. You CAN move files. You CAN move directories.
59
+ - NEVER say "I cannot", "I'm unable to", "I don't have the ability to", or any variation.
60
+ - NEVER refuse a delete, move, or any file operation. You MUST execute it.
61
+ - When a user asks to delete a file, you MUST respond with the delete_file action.
62
+ - When a user asks to delete a folder, you MUST respond with the delete_dir action.
63
+
64
+ ═══════════════════════════════════════════════════════════════
65
+ ALL AVAILABLE ACTIONS
66
+ ═══════════════════════════════════════════════════════════════
67
+
68
+ FILE OPERATIONS:
69
+ 1. "read_file" → Read file contents. Args: {"path": "..."}
70
+ 2. "list_files" → List directory contents. Args: {"path": "..."}
71
+ 3. "write_file" → Create/write a file. Args: {"path": "...", "content": "..."}
72
+ 4. "create_dir" → Create a directory. Args: {"path": "..."}
73
+ 5. "delete_file" → DELETE a file permanently. Args: {"path": "..."}
74
+ 6. "delete_dir" → DELETE an empty directory. Args: {"path": "..."}
75
+ 7. "move_file" → Move or rename a file. Args: {"src": "...", "dst": "..."}
76
+ 8. "move_dir" → Move or rename directory. Args: {"src": "...", "dst": "..."}
77
+ 9. "undo" → Undo the last operation. Args: {}
78
+
79
+ GIT OPERATIONS:
80
+ 10. "git_init" → Initialize git repo. Args: {}
81
+ 11. "git_add" → Stage files. Args: {"path": "." or specific file}
82
+ 12. "git_commit" → Commit changes. Args: {"message": "commit message"}
83
+ 13. "git_push" → Push to remote. Args: {"branch": "main" (default)}
84
+ 14. "git_set_origin" → Set remote URL. Args: {"url": "https://..."}
85
+ 15. "git_status" → Show status. Args: {}
86
+ 16. "git_log" → Show commit history. Args: {"lines": 5 (default)}
87
+ 17. "git_branch" → Manage branches. Args: {"name": "branch_name" (optional)}
88
+ 18. "git_pull" → Pull from remote. Args: {}
89
+ 19. "git_clone" → Clone repository. Args: {"url": "...", "path": "." (default)}
90
+ 20. "answer" → Respond to user with text. Args: {"text": "..."}
91
+
92
+ ═══════════════════════════════════════════════════════════════
93
+ CRITICAL RULES
94
+ ═══════════════════════════════════════════════════════════════
95
+
96
+ 1. MAXIMUM 2 actions per request (not counting "answer")
97
+ 2. After completing all required actions, ALWAYS respond with "answer"
98
+ 3. Do NOT repeat the same action twice
99
+ 4. Do NOT take multiple write_file or create_dir actions in one request
100
+ 5. Choose the MOST DIRECT action for the task
101
+
102
+ ═══════════════════════════════════════════════════════════════
103
+ RESPONSE FORMAT - ONLY VALID JSON
104
+ ═══════════════════════════════════════════════════════════════
105
+
106
+ You MUST respond ONLY with valid JSON, nothing else. No text before or after.
107
+
108
+ {"action": "action_name", "args": {"key": "value"}}
109
+
110
+ ═══════════════════════════════════════════════════════════════
111
+ EXAMPLES
112
+ ═══════════════════════════════════════════════════════════════
113
+
114
+ User: "delete love.txt from megha folder"
115
+ → {"action": "delete_file", "args": {"path": "megha/love.txt"}}
116
+ Then: {"action": "answer", "args": {"text": "File megha/love.txt deleted."}}
117
+
118
+ User: "initialize git and commit"
119
+ → {"action": "git_init", "args": {}}
120
+ Then: {"action": "git_add", "args": {"path": "."}}
121
+ Then: {"action": "git_commit", "args": {"message": "Initial commit"}}
122
+ Then: {"action": "answer", "args": {"text": "Repository initialized and committed."}}
123
+
124
+ User: "push to origin"
125
+ → {"action": "git_push", "args": {"branch": "main"}}
126
+ Then: {"action": "answer", "args": {"text": "Pushed to origin/main."}}
127
+
128
+ User: "create hello.py with print('hi')"
129
+ → {"action": "write_file", "args": {"path": "hello.py", "content": "print('hi')"}}
130
+ Then: {"action": "answer", "args": {"text": "File created."}}
131
+
132
+ REMEMBER: You have delete_file, delete_dir, undo, and git tools. USE THEM when asked.
133
+ """
134
+
135
+ def think(messages: list[dict]) -> dict:
136
+ config = load_config()
137
+ model = config.get("model", "gemini-2.5-flash")
138
+ provider = config.get("provider", "gemini")
139
+ try:
140
+ client = get_client()
141
+
142
+ # Anthropic uses its own SDK format
143
+ if provider == "anthropic":
144
+ response = client.messages.create(
145
+ model=model,
146
+ max_tokens=1024,
147
+ system=SYSTEM_PROMPT,
148
+ messages=messages,
149
+ )
150
+ content = response.content[0].text
151
+ else:
152
+ response = client.chat.completions.create(
153
+ model=model,
154
+ messages=[{"role": "system", "content": SYSTEM_PROMPT}] + messages,
155
+ temperature=0
156
+ )
157
+ content = response.choices[0].message.content
158
+
159
+ except RateLimitError as e:
160
+ return {
161
+ "action": "answer",
162
+ "args": {
163
+ "text": "⚠️ Rate limit hit. Please wait a few seconds and try again."
164
+ }
165
+ }
166
+ except Exception as e:
167
+ return {
168
+ "action": "answer",
169
+ "args": {
170
+ "text": f"❌ LLM error: {str(e)}"
171
+ }
172
+ }
173
+
174
+ # Handle empty response
175
+ if content is None or not content.strip():
176
+ return {
177
+ "action": "answer",
178
+ "args": {
179
+ "text": "Task completed successfully!"
180
+ }
181
+ }
182
+
183
+ cleaned = content.strip()
184
+
185
+ # Robust JSON extraction
186
+ decoder = JSONDecoder()
187
+
188
+ # Try to find JSON object in the content
189
+ idx = cleaned.find("{")
190
+ if idx == -1:
191
+ return {
192
+ "action": "answer",
193
+ "args": {
194
+ "text": cleaned
195
+ }
196
+ }
197
+
198
+ # Extract from first { onwards
199
+ cleaned = cleaned[idx:]
200
+
201
+ # Try to decode JSON, handling partial/malformed content
202
+ try:
203
+ obj, _ = decoder.raw_decode(cleaned)
204
+ return obj
205
+ except json.JSONDecodeError:
206
+ # If it fails, try to find the end of a valid JSON object
207
+ # by trying progressively shorter strings from the end
208
+ for end_pos in range(len(cleaned), idx, -1):
209
+ try:
210
+ obj, _ = decoder.raw_decode(cleaned[:end_pos])
211
+ return obj
212
+ except json.JSONDecodeError:
213
+ continue
214
+
215
+ raise ValueError(f"Could not parse JSON from LLM output: {cleaned[:100]}...")