devagent-cli 3.2.1__tar.gz → 3.2.3__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.
- {devagent_cli-3.2.1/devagent_cli.egg-info → devagent_cli-3.2.3}/PKG-INFO +11 -9
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/README.md +10 -8
- devagent_cli-3.2.3/devagent/__init__.py +1 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/app/agent.py +21 -1
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/app/state.py +1 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/cli.py +53 -6
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/utils/config.py +4 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/utils/metrics.py +2 -0
- devagent_cli-3.2.3/devagent/utils/safety.py +90 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3/devagent_cli.egg-info}/PKG-INFO +11 -9
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent_cli.egg-info/SOURCES.txt +1 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/pyproject.toml +1 -1
- devagent_cli-3.2.1/devagent/__init__.py +0 -1
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/LICENSE +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/app/__init__.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/app/llm.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/app/memory.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/app/patcher.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/app/planner.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/app/reviewer.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/app/sandbox.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/tools/__init__.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/tools/benchmark_runner.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/tools/file_ops.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/tools/git_tools.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/tools/linter.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/tools/search.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/tools/semantic_search.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/tools/surgical_patcher.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/tools/test_runner.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/utils/__init__.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent/utils/logger.py +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent_cli.egg-info/dependency_links.txt +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent_cli.egg-info/entry_points.txt +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent_cli.egg-info/requires.txt +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/devagent_cli.egg-info/top_level.txt +0 -0
- {devagent_cli-3.2.1 → devagent_cli-3.2.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devagent-cli
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.3
|
|
4
4
|
Summary: Professional Local autonomous coding agent powered by Ollama
|
|
5
5
|
Author: Vedant Jadhav
|
|
6
6
|
License: MIT
|
|
@@ -31,6 +31,7 @@ Dynamic: license-file
|
|
|
31
31
|
### A Lightweight Local Open-Source Miniature of Claude Code CLI
|
|
32
32
|
|
|
33
33
|
[](https://opensource.org/licenses/MIT)
|
|
34
|
+
[](https://badge.fury.io/py/devagent-cli)
|
|
34
35
|
[](https://www.python.org/)
|
|
35
36
|
[](https://ollama.ai)
|
|
36
37
|
[](CONTRIBUTING.md)
|
|
@@ -140,16 +141,16 @@ devagent run --task "Fix the divide-by-zero bug" --root ./demo_project
|
|
|
140
141
|
| `devagent models` | List available Ollama models |
|
|
141
142
|
| `devagent version` | Show current version |
|
|
142
143
|
|
|
143
|
-
###
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
- **
|
|
148
|
-
- **Forensic Test Detection**:
|
|
149
|
-
- **
|
|
144
|
+
### 🛡️ Reliability & Safety (v3.2.1+)
|
|
145
|
+
DevAgent is built for **Enterprise-grade safety**:
|
|
146
|
+
- **Dry Run Mode**: Use `--dry-run` to see what the agent *would* do without touching your files.
|
|
147
|
+
- **Auto-Snapshot**: Creates a safety restore point before every run.
|
|
148
|
+
- **Rollback**: Revert the last agent changes instantly with `devagent rollback`.
|
|
149
|
+
- **Forensic Test Detection**: Detects successful test runs even in noisy environments.
|
|
150
|
+
- **Path Anchoring**: Automatically corrects "root hallucinations" for subdirectories.
|
|
150
151
|
|
|
151
152
|
#### 🕹️ Interactive Mode
|
|
152
|
-
Run with `--interactive` (or `-i`) to review diffs before they are applied to your project.
|
|
153
|
+
Run with `--interactive` (or `-i`) to review colorized diffs before they are applied to your project.
|
|
153
154
|
```bash
|
|
154
155
|
devagent run --task "Fix bug" --interactive
|
|
155
156
|
```
|
|
@@ -161,6 +162,7 @@ devagent run --task "Fix bug" --interactive
|
|
|
161
162
|
```mermaid
|
|
162
163
|
graph TD
|
|
163
164
|
CLI[DevAgent CLI] --> Orchestrator[ReAct Orchestrator]
|
|
165
|
+
Orchestrator --> Safety[Safety Manager: Snapshots]
|
|
164
166
|
Orchestrator --> Memory[Working Memory]
|
|
165
167
|
Orchestrator --> Retrieval[Semantic Retrieval FAISS]
|
|
166
168
|
Orchestrator --> Tools[Tool Suite: pytest, ripgrep, git]
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
### A Lightweight Local Open-Source Miniature of Claude Code CLI
|
|
6
6
|
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://badge.fury.io/py/devagent-cli)
|
|
8
9
|
[](https://www.python.org/)
|
|
9
10
|
[](https://ollama.ai)
|
|
10
11
|
[](CONTRIBUTING.md)
|
|
@@ -114,16 +115,16 @@ devagent run --task "Fix the divide-by-zero bug" --root ./demo_project
|
|
|
114
115
|
| `devagent models` | List available Ollama models |
|
|
115
116
|
| `devagent version` | Show current version |
|
|
116
117
|
|
|
117
|
-
###
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
- **
|
|
122
|
-
- **Forensic Test Detection**:
|
|
123
|
-
- **
|
|
118
|
+
### 🛡️ Reliability & Safety (v3.2.1+)
|
|
119
|
+
DevAgent is built for **Enterprise-grade safety**:
|
|
120
|
+
- **Dry Run Mode**: Use `--dry-run` to see what the agent *would* do without touching your files.
|
|
121
|
+
- **Auto-Snapshot**: Creates a safety restore point before every run.
|
|
122
|
+
- **Rollback**: Revert the last agent changes instantly with `devagent rollback`.
|
|
123
|
+
- **Forensic Test Detection**: Detects successful test runs even in noisy environments.
|
|
124
|
+
- **Path Anchoring**: Automatically corrects "root hallucinations" for subdirectories.
|
|
124
125
|
|
|
125
126
|
#### 🕹️ Interactive Mode
|
|
126
|
-
Run with `--interactive` (or `-i`) to review diffs before they are applied to your project.
|
|
127
|
+
Run with `--interactive` (or `-i`) to review colorized diffs before they are applied to your project.
|
|
127
128
|
```bash
|
|
128
129
|
devagent run --task "Fix bug" --interactive
|
|
129
130
|
```
|
|
@@ -135,6 +136,7 @@ devagent run --task "Fix bug" --interactive
|
|
|
135
136
|
```mermaid
|
|
136
137
|
graph TD
|
|
137
138
|
CLI[DevAgent CLI] --> Orchestrator[ReAct Orchestrator]
|
|
139
|
+
Orchestrator --> Safety[Safety Manager: Snapshots]
|
|
138
140
|
Orchestrator --> Memory[Working Memory]
|
|
139
141
|
Orchestrator --> Retrieval[Semantic Retrieval FAISS]
|
|
140
142
|
Orchestrator --> Tools[Tool Suite: pytest, ripgrep, git]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "3.2.3"
|
|
@@ -114,13 +114,14 @@ EXTRACT_ACTION_PATTERN = re.compile(
|
|
|
114
114
|
class Agent:
|
|
115
115
|
"""ReAct agent with planner, retrieval, self-review, and sandbox support."""
|
|
116
116
|
|
|
117
|
-
def __init__(self, task: str, project_root: str = ".", max_steps: int = 5):
|
|
117
|
+
def __init__(self, task: str, project_root: str = ".", max_steps: int = 5, dry_run: bool = False):
|
|
118
118
|
self.state = AgentState(
|
|
119
119
|
task=task,
|
|
120
120
|
project_root=os.path.abspath(project_root),
|
|
121
121
|
max_steps=max_steps,
|
|
122
122
|
working_root=os.path.abspath(project_root),
|
|
123
123
|
)
|
|
124
|
+
self.dry_run = dry_run
|
|
124
125
|
self.logger = AgentLogger(log_dir=os.path.join(project_root, "logs"))
|
|
125
126
|
self.metrics = RunMetrics(task=task)
|
|
126
127
|
self.memory = WorkingMemory()
|
|
@@ -279,6 +280,12 @@ class Agent:
|
|
|
279
280
|
observation = self._execute_action(action_name, action_arg)
|
|
280
281
|
self.state.last_observation = observation
|
|
281
282
|
self.state.observations.append(observation[:2000])
|
|
283
|
+
|
|
284
|
+
self.state.explanations.append({
|
|
285
|
+
"type": "action",
|
|
286
|
+
"action": f"{action_name}: {action_arg}",
|
|
287
|
+
"reason": thought
|
|
288
|
+
})
|
|
282
289
|
|
|
283
290
|
# STEP 3 — GENERATE FIX (if we have a file in context)
|
|
284
291
|
code_fix = ""
|
|
@@ -554,8 +561,21 @@ class Agent:
|
|
|
554
561
|
print(f" [REVIEW] #{revision + 1}: {review_text[:100]}")
|
|
555
562
|
|
|
556
563
|
if approved:
|
|
564
|
+
self.state.explanations.append({
|
|
565
|
+
"type": "review",
|
|
566
|
+
"file": self.state.current_file,
|
|
567
|
+
"reason": review_text,
|
|
568
|
+
"status": "APPROVED"
|
|
569
|
+
})
|
|
557
570
|
return code_fix, review_text
|
|
558
571
|
|
|
572
|
+
self.metrics.patch_rejections += 1
|
|
573
|
+
self.state.explanations.append({
|
|
574
|
+
"type": "review",
|
|
575
|
+
"file": self.state.current_file,
|
|
576
|
+
"reason": review_text,
|
|
577
|
+
"status": "REJECTED"
|
|
578
|
+
})
|
|
559
579
|
print(f" [REVISE] Revising code (attempt {revision + 1})...")
|
|
560
580
|
code_fix = revise_code(code_fix, review_text, self.state.task)
|
|
561
581
|
code_fix = self._strip_code_fences(code_fix)
|
|
@@ -73,6 +73,7 @@ class AgentState:
|
|
|
73
73
|
# -- Trust & Confidence --
|
|
74
74
|
confidence_score: float = 0.0
|
|
75
75
|
confidence_reasons: list[str] = field(default_factory=list)
|
|
76
|
+
explanations: list[dict[str, str]] = field(default_factory=list)
|
|
76
77
|
|
|
77
78
|
def to_dict(self) -> dict[str, Any]:
|
|
78
79
|
"""Return a JSON-serialisable snapshot of the current state."""
|
|
@@ -92,15 +92,24 @@ def cmd_run(args):
|
|
|
92
92
|
# Sandbox setup
|
|
93
93
|
sandbox = None
|
|
94
94
|
working_root = root
|
|
95
|
-
if config.sandbox:
|
|
95
|
+
if config.sandbox or config.dry_run:
|
|
96
|
+
if config.dry_run:
|
|
97
|
+
console.print("[bold yellow][DRY RUN][/bold yellow] Agent will work in an isolated sandbox. No changes will be applied.")
|
|
96
98
|
sandbox = SandboxManager(root)
|
|
97
99
|
working_root = sandbox.create()
|
|
98
100
|
|
|
101
|
+
# Safety Snapshot
|
|
102
|
+
if not config.dry_run:
|
|
103
|
+
from devagent.utils.safety import SafetyManager
|
|
104
|
+
safety = SafetyManager(root)
|
|
105
|
+
safety.create_snapshot(task_id=config.task[:10].replace(" ", "_"))
|
|
106
|
+
|
|
99
107
|
# Run agent
|
|
100
108
|
agent = Agent(
|
|
101
109
|
task=config.task,
|
|
102
110
|
project_root=working_root,
|
|
103
111
|
max_steps=config.max_steps,
|
|
112
|
+
dry_run=config.dry_run
|
|
104
113
|
)
|
|
105
114
|
|
|
106
115
|
start_time = time.time()
|
|
@@ -133,12 +142,35 @@ def cmd_run(args):
|
|
|
133
142
|
if final_state.confidence_reasons:
|
|
134
143
|
console.print("\n[bold]Confidence Breakdown:[/bold]")
|
|
135
144
|
for reason in final_state.confidence_reasons:
|
|
136
|
-
console.print(f" [
|
|
137
|
-
|
|
138
|
-
#
|
|
145
|
+
console.print(f" [green]✓[/green] {reason}")
|
|
146
|
+
|
|
147
|
+
# Explain Mode
|
|
148
|
+
if config.explain and final_state.explanations:
|
|
149
|
+
console.print("\n" + "=" * 60)
|
|
150
|
+
console.print(" [bold cyan]TRACEABILITY REPORT (EXPLAIN MODE)[/bold cyan]")
|
|
151
|
+
console.print("=" * 60)
|
|
152
|
+
for exp in final_state.explanations:
|
|
153
|
+
if exp["type"] == "action":
|
|
154
|
+
console.print(f"\n[bold yellow]Selection:[/bold yellow] {exp['action']}")
|
|
155
|
+
console.print(f" [dim]Why:[/dim] {exp['reason']}")
|
|
156
|
+
elif exp["type"] == "review":
|
|
157
|
+
status_color = "green" if exp["status"] == "APPROVED" else "red"
|
|
158
|
+
console.print(f"\n[bold {status_color}]Review:[/bold {status_color}] {exp['file']} ({exp['status']})")
|
|
159
|
+
console.print(f" [dim]Logic:[/dim] {exp['reason']}")
|
|
160
|
+
|
|
161
|
+
# Sandbox apply / Dry Run
|
|
139
162
|
if sandbox and sandbox.is_active:
|
|
163
|
+
if config.dry_run:
|
|
164
|
+
console.print("\n[bold yellow][DRY RUN][/bold yellow] Completed. No changes applied.")
|
|
165
|
+
# Show diffs anyway to show what would have happened
|
|
166
|
+
for i, patch in enumerate(final_state.patches_applied):
|
|
167
|
+
console.print(f"\n[bold]Proposed Patch #{i+1}[/bold] for [cyan]{patch.get('file', 'unknown')}[/cyan]:")
|
|
168
|
+
console.print(f"[dim]{patch.get('diff', 'No diff available')}[/dim]")
|
|
169
|
+
sandbox.destroy()
|
|
170
|
+
return 0
|
|
171
|
+
|
|
140
172
|
if final_state.status == "success":
|
|
141
|
-
if
|
|
173
|
+
if config.interactive:
|
|
142
174
|
console.print("\n[bold yellow][INTERACTIVE][/bold yellow] Reviewing changes...")
|
|
143
175
|
# Show diff for each applied patch
|
|
144
176
|
for i, patch in enumerate(final_state.patches_applied):
|
|
@@ -159,7 +191,7 @@ def cmd_run(args):
|
|
|
159
191
|
sandbox.destroy()
|
|
160
192
|
|
|
161
193
|
# Git operations
|
|
162
|
-
if final_state.status == "success" and config.auto_commit:
|
|
194
|
+
if final_state.status == "success" and config.auto_commit and not config.dry_run:
|
|
163
195
|
_handle_git(root, config)
|
|
164
196
|
|
|
165
197
|
return 0 if final_state.status == "success" else 1
|
|
@@ -231,6 +263,13 @@ def cmd_models(args):
|
|
|
231
263
|
console.print("[red][ERROR][/red] Could not list Ollama models.")
|
|
232
264
|
return 0
|
|
233
265
|
|
|
266
|
+
def cmd_rollback(args):
|
|
267
|
+
"""Implementation of 'devagent rollback' command."""
|
|
268
|
+
from devagent.utils.safety import SafetyManager
|
|
269
|
+
root = os.path.abspath(getattr(args, "root", "."))
|
|
270
|
+
safety = SafetyManager(root)
|
|
271
|
+
return 0 if safety.rollback() else 1
|
|
272
|
+
|
|
234
273
|
def main():
|
|
235
274
|
parser = argparse.ArgumentParser(description="DevAgent CLI — Professional local coding agent.")
|
|
236
275
|
parser.add_argument("--version", action="version", version=f"DevAgent v{__version__}")
|
|
@@ -247,6 +286,8 @@ def main():
|
|
|
247
286
|
run_parser.add_argument("--auto-commit", action="store_true", help="Auto-commit on success")
|
|
248
287
|
run_parser.add_argument("--auto-push", action="store_true", help="Auto-push after commit")
|
|
249
288
|
run_parser.add_argument("--interactive", "-i", action="store_true", help="Review changes before applying")
|
|
289
|
+
run_parser.add_argument("--dry-run", action="store_true", help="Run without applying any changes")
|
|
290
|
+
run_parser.add_argument("--explain", action="store_true", help="Explain why files were chosen and patches applied")
|
|
250
291
|
run_parser.add_argument("--verbose", action="store_true", help="Verbose output")
|
|
251
292
|
|
|
252
293
|
# Command: benchmark
|
|
@@ -260,6 +301,10 @@ def main():
|
|
|
260
301
|
# Command: models
|
|
261
302
|
subparsers.add_parser("models", help="List installed Ollama models")
|
|
262
303
|
|
|
304
|
+
# Command: rollback
|
|
305
|
+
roll_parser = subparsers.add_parser("rollback", help="Revert the last agent changes")
|
|
306
|
+
roll_parser.add_argument("--root", "-r", default=".", help="Project root")
|
|
307
|
+
|
|
263
308
|
# Command: version
|
|
264
309
|
subparsers.add_parser("version", help="Show version")
|
|
265
310
|
|
|
@@ -273,6 +318,8 @@ def main():
|
|
|
273
318
|
sys.exit(cmd_doctor(args))
|
|
274
319
|
elif args.command == "models":
|
|
275
320
|
sys.exit(cmd_models(args))
|
|
321
|
+
elif args.command == "rollback":
|
|
322
|
+
sys.exit(cmd_rollback(args))
|
|
276
323
|
elif args.command == "version":
|
|
277
324
|
console.print(f"DevAgent CLI v{__version__}")
|
|
278
325
|
else:
|
|
@@ -53,6 +53,8 @@ class AgentConfig:
|
|
|
53
53
|
|
|
54
54
|
# ── Feature flags ──
|
|
55
55
|
sandbox: bool = True
|
|
56
|
+
dry_run: bool = False
|
|
57
|
+
explain: bool = False
|
|
56
58
|
auto_commit: bool = False
|
|
57
59
|
auto_push: bool = False # DISABLED by default — safety first
|
|
58
60
|
benchmark: bool = False
|
|
@@ -98,6 +100,8 @@ class AgentConfig:
|
|
|
98
100
|
max_steps=getattr(args, "max_steps", 5),
|
|
99
101
|
verbose=getattr(args, "verbose", False),
|
|
100
102
|
sandbox=getattr(args, "sandbox", True),
|
|
103
|
+
dry_run=getattr(args, "dry_run", False),
|
|
104
|
+
explain=getattr(args, "explain", False),
|
|
101
105
|
auto_commit=getattr(args, "auto_commit", False),
|
|
102
106
|
auto_push=getattr(args, "auto_push", False),
|
|
103
107
|
benchmark=getattr(args, "benchmark", False),
|
|
@@ -36,6 +36,7 @@ class RunMetrics:
|
|
|
36
36
|
end_time: float = 0.0
|
|
37
37
|
total_steps: int = 0
|
|
38
38
|
retries: int = 0
|
|
39
|
+
patch_rejections: int = 0
|
|
39
40
|
successes: int = 0
|
|
40
41
|
failures: int = 0
|
|
41
42
|
total_latency_s: float = 0.0
|
|
@@ -88,6 +89,7 @@ class RunMetrics:
|
|
|
88
89
|
"task": self.task[:100],
|
|
89
90
|
"total_steps": self.total_steps,
|
|
90
91
|
"retries": self.retries,
|
|
92
|
+
"patch_rejections": self.patch_rejections,
|
|
91
93
|
"successes": self.successes,
|
|
92
94
|
"failures": self.failures,
|
|
93
95
|
"total_latency_s": round(self.total_latency_s, 2),
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Safety Manager — handles snapshots and rollbacks.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import time
|
|
8
|
+
import subprocess
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from devagent.tools.git_tools import is_git_repo
|
|
11
|
+
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
class SafetyManager:
|
|
15
|
+
def __init__(self, project_root: str):
|
|
16
|
+
self.root = os.path.abspath(project_root)
|
|
17
|
+
self.snapshot_dir = os.path.join(self.root, ".devagent_backups")
|
|
18
|
+
|
|
19
|
+
def create_snapshot(self, task_id: str = "latest") -> str:
|
|
20
|
+
"""Create a safety snapshot of the current project state."""
|
|
21
|
+
if is_git_repo(self.root):
|
|
22
|
+
# For git repos, we rely on the user's ability to git checkout
|
|
23
|
+
# but we could also do a 'git stash' here for safety.
|
|
24
|
+
return "git"
|
|
25
|
+
|
|
26
|
+
# For non-git repos, we do a physical backup of .py files
|
|
27
|
+
os.makedirs(self.snapshot_dir, exist_ok=True)
|
|
28
|
+
timestamp = int(time.time())
|
|
29
|
+
dest = os.path.join(self.snapshot_dir, f"{task_id}_{timestamp}")
|
|
30
|
+
|
|
31
|
+
# Simple copy of python files
|
|
32
|
+
os.makedirs(dest, exist_ok=True)
|
|
33
|
+
for root, _, files in os.walk(self.root):
|
|
34
|
+
if ".devagent" in root or "venv" in root:
|
|
35
|
+
continue
|
|
36
|
+
for f in files:
|
|
37
|
+
if f.endswith(".py"):
|
|
38
|
+
src_file = os.path.join(root, f)
|
|
39
|
+
rel_path = os.path.relpath(src_file, self.root)
|
|
40
|
+
target_file = os.path.join(dest, rel_path)
|
|
41
|
+
os.makedirs(os.path.dirname(target_file), exist_ok=True)
|
|
42
|
+
shutil.copy2(src_file, target_file)
|
|
43
|
+
|
|
44
|
+
# Save metadata
|
|
45
|
+
metadata = {
|
|
46
|
+
"task_id": task_id,
|
|
47
|
+
"timestamp": timestamp,
|
|
48
|
+
"root": self.root
|
|
49
|
+
}
|
|
50
|
+
with open(os.path.join(dest, "metadata.json"), "w") as f:
|
|
51
|
+
import json
|
|
52
|
+
json.dump(metadata, f, indent=2)
|
|
53
|
+
|
|
54
|
+
return dest
|
|
55
|
+
|
|
56
|
+
def rollback(self, snapshot_id: str = "latest") -> bool:
|
|
57
|
+
"""Rollback the project to a previous snapshot."""
|
|
58
|
+
if is_git_repo(self.root):
|
|
59
|
+
console.print("[bold yellow][SAFETY][/bold yellow] This is a Git repository. Reverting all local changes...")
|
|
60
|
+
try:
|
|
61
|
+
subprocess.run(["git", "checkout", "."], cwd=self.root, check=True, capture_output=True)
|
|
62
|
+
console.print("[bold green]Git rollback complete.[/bold green]")
|
|
63
|
+
return True
|
|
64
|
+
except Exception as e:
|
|
65
|
+
console.print(f"[bold red][ERROR][/bold red] Git rollback failed: {e}")
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
# Find latest snapshot if not specified
|
|
69
|
+
if not os.path.exists(self.snapshot_dir):
|
|
70
|
+
console.print("[bold red][ERROR][/bold red] No snapshots found.")
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
snapshots = sorted(os.listdir(self.snapshot_dir))
|
|
74
|
+
if not snapshots:
|
|
75
|
+
console.print("[bold red][ERROR][/bold red] No snapshots found.")
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
target = os.path.join(self.snapshot_dir, snapshots[-1])
|
|
79
|
+
console.print(f"[bold yellow][SAFETY][/bold yellow] Rolling back from {target}...")
|
|
80
|
+
|
|
81
|
+
# Restore files
|
|
82
|
+
for root, _, files in os.walk(target):
|
|
83
|
+
for f in files:
|
|
84
|
+
src_file = os.path.join(root, f)
|
|
85
|
+
rel_path = os.path.relpath(src_file, target)
|
|
86
|
+
dest_file = os.path.join(self.root, rel_path)
|
|
87
|
+
shutil.copy2(src_file, dest_file)
|
|
88
|
+
|
|
89
|
+
console.print("[bold green]Rollback complete.[/bold green]")
|
|
90
|
+
return True
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devagent-cli
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.3
|
|
4
4
|
Summary: Professional Local autonomous coding agent powered by Ollama
|
|
5
5
|
Author: Vedant Jadhav
|
|
6
6
|
License: MIT
|
|
@@ -31,6 +31,7 @@ Dynamic: license-file
|
|
|
31
31
|
### A Lightweight Local Open-Source Miniature of Claude Code CLI
|
|
32
32
|
|
|
33
33
|
[](https://opensource.org/licenses/MIT)
|
|
34
|
+
[](https://badge.fury.io/py/devagent-cli)
|
|
34
35
|
[](https://www.python.org/)
|
|
35
36
|
[](https://ollama.ai)
|
|
36
37
|
[](CONTRIBUTING.md)
|
|
@@ -140,16 +141,16 @@ devagent run --task "Fix the divide-by-zero bug" --root ./demo_project
|
|
|
140
141
|
| `devagent models` | List available Ollama models |
|
|
141
142
|
| `devagent version` | Show current version |
|
|
142
143
|
|
|
143
|
-
###
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
- **
|
|
148
|
-
- **Forensic Test Detection**:
|
|
149
|
-
- **
|
|
144
|
+
### 🛡️ Reliability & Safety (v3.2.1+)
|
|
145
|
+
DevAgent is built for **Enterprise-grade safety**:
|
|
146
|
+
- **Dry Run Mode**: Use `--dry-run` to see what the agent *would* do without touching your files.
|
|
147
|
+
- **Auto-Snapshot**: Creates a safety restore point before every run.
|
|
148
|
+
- **Rollback**: Revert the last agent changes instantly with `devagent rollback`.
|
|
149
|
+
- **Forensic Test Detection**: Detects successful test runs even in noisy environments.
|
|
150
|
+
- **Path Anchoring**: Automatically corrects "root hallucinations" for subdirectories.
|
|
150
151
|
|
|
151
152
|
#### 🕹️ Interactive Mode
|
|
152
|
-
Run with `--interactive` (or `-i`) to review diffs before they are applied to your project.
|
|
153
|
+
Run with `--interactive` (or `-i`) to review colorized diffs before they are applied to your project.
|
|
153
154
|
```bash
|
|
154
155
|
devagent run --task "Fix bug" --interactive
|
|
155
156
|
```
|
|
@@ -161,6 +162,7 @@ devagent run --task "Fix bug" --interactive
|
|
|
161
162
|
```mermaid
|
|
162
163
|
graph TD
|
|
163
164
|
CLI[DevAgent CLI] --> Orchestrator[ReAct Orchestrator]
|
|
165
|
+
Orchestrator --> Safety[Safety Manager: Snapshots]
|
|
164
166
|
Orchestrator --> Memory[Working Memory]
|
|
165
167
|
Orchestrator --> Retrieval[Semantic Retrieval FAISS]
|
|
166
168
|
Orchestrator --> Tools[Tool Suite: pytest, ripgrep, git]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "3.2.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|