forgecodecli 0.1.0__tar.gz → 1.0.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,185 @@
1
+ Metadata-Version: 2.4
2
+ Name: forgecodecli
3
+ Version: 1.0.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
+ ## What's Available
161
+
162
+ - File operations (read, list, create, write, delete, move)
163
+ - Undo functionality - Stack-based operation reversal
164
+ - Git operations - init, add, commit, push, pull, status, log, branch, clone, set_origin
165
+ - Multi-provider support - Gemini, OpenAI, Anthropic, Groq
166
+ - Model selection - Choose specific models per provider
167
+ - Auto-install SDKs - Anthropic SDK installs on demand
168
+ - Proper conversation flow with max actions per request
169
+
170
+ ## Coming Soon
171
+
172
+ - Copy files/directories
173
+ - Full undo/redo history (not just last operation)
174
+ - File search capabilities
175
+ - Code generation templates
176
+ - Batch operations
177
+ - Backup/snapshot functionality
178
+
179
+ ## License
180
+
181
+ MIT License
182
+
183
+ ## Author
184
+
185
+ Built by Sudhanshu
@@ -0,0 +1,167 @@
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
+ ## What's Available
143
+
144
+ - File operations (read, list, create, write, delete, move)
145
+ - Undo functionality - Stack-based operation reversal
146
+ - Git operations - init, add, commit, push, pull, status, log, branch, clone, set_origin
147
+ - Multi-provider support - Gemini, OpenAI, Anthropic, Groq
148
+ - Model selection - Choose specific models per provider
149
+ - Auto-install SDKs - Anthropic SDK installs on demand
150
+ - Proper conversation flow with max actions per request
151
+
152
+ ## Coming Soon
153
+
154
+ - Copy files/directories
155
+ - Full undo/redo history (not just last operation)
156
+ - File search capabilities
157
+ - Code generation templates
158
+ - Batch operations
159
+ - Backup/snapshot functionality
160
+
161
+ ## License
162
+
163
+ MIT License
164
+
165
+ ## Author
166
+
167
+ 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]}...")