devagent-cli 3.2.2__tar.gz → 3.3.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.
Files changed (39) hide show
  1. {devagent_cli-3.2.2/devagent_cli.egg-info → devagent_cli-3.3.0}/PKG-INFO +22 -25
  2. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/README.md +20 -23
  3. devagent_cli-3.3.0/devagent/__init__.py +1 -0
  4. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/app/agent.py +35 -20
  5. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/app/state.py +1 -0
  6. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/cli.py +16 -1
  7. devagent_cli-3.3.0/devagent/tools/file_map.py +21 -0
  8. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/tools/file_ops.py +22 -2
  9. devagent_cli-3.3.0/devagent/utils/ast_utils.py +83 -0
  10. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/utils/config.py +2 -0
  11. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/utils/safety.py +20 -3
  12. {devagent_cli-3.2.2 → devagent_cli-3.3.0/devagent_cli.egg-info}/PKG-INFO +22 -25
  13. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent_cli.egg-info/SOURCES.txt +2 -0
  14. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/pyproject.toml +2 -2
  15. devagent_cli-3.2.2/devagent/__init__.py +0 -1
  16. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/LICENSE +0 -0
  17. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/app/__init__.py +0 -0
  18. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/app/llm.py +0 -0
  19. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/app/memory.py +0 -0
  20. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/app/patcher.py +0 -0
  21. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/app/planner.py +0 -0
  22. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/app/reviewer.py +0 -0
  23. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/app/sandbox.py +0 -0
  24. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/tools/__init__.py +0 -0
  25. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/tools/benchmark_runner.py +0 -0
  26. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/tools/git_tools.py +0 -0
  27. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/tools/linter.py +0 -0
  28. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/tools/search.py +0 -0
  29. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/tools/semantic_search.py +0 -0
  30. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/tools/surgical_patcher.py +0 -0
  31. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/tools/test_runner.py +0 -0
  32. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/utils/__init__.py +0 -0
  33. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/utils/logger.py +0 -0
  34. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent/utils/metrics.py +0 -0
  35. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent_cli.egg-info/dependency_links.txt +0 -0
  36. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent_cli.egg-info/entry_points.txt +0 -0
  37. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent_cli.egg-info/requires.txt +0 -0
  38. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/devagent_cli.egg-info/top_level.txt +0 -0
  39. {devagent_cli-3.2.2 → devagent_cli-3.3.0}/setup.cfg +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devagent-cli
3
- Version: 3.2.2
4
- Summary: Professional Local autonomous coding agent powered by Ollama
3
+ Version: 3.3.0
4
+ Summary: A local autonomous coding agent CLI powered by Ollama.
5
5
  Author: Vedant Jadhav
6
6
  License: MIT
7
7
  Keywords: ai,agent,coding,ollama,local,devagent,devagent-cli
@@ -49,26 +49,23 @@ Dynamic: license-file
49
49
 
50
50
  </div>
51
51
 
52
- ## 🤔 Why DevAgent?
52
+ ## 🛡️ Why DevAgent?
53
53
 
54
- Most AI coding tools are **chatbots** they suggest code, you copy-paste, you pray.
54
+ DevAgent is built on a "Safety-First" architecture for **high-integrity developer infrastructure**. Unlike chatbots that guess code, DevAgent is a local autonomous agent that operates within a strictly observed environment.
55
55
 
56
- DevAgent is a **real agent** with a retrieval-first, tool-grounded architecture:
56
+ ### 📊 Empirical Validation
57
+ We don't hide our limitations. DevAgent v3.2.3 has been stress-tested against real-world, "messy" repositories, providing a transparent **[Benchmark Report](docs/benchmarks.md)**.
57
58
 
58
- | | Chatbot | DevAgent |
59
+ | | Other Agents | DevAgent |
59
60
  |---|---|---|
60
- | Searches your codebase | ❌ | ✅ ripgrep + semantic search |
61
- | Retrieves relevant code | ❌ | ✅ FAISS embeddings |
62
- | Plans before coding | ❌ | ✅ Planner layer |
63
- | Generates patches | ❌ | ✅ Unified diffs |
64
- | Reviews its own output | ❌ | ✅ Self-critique loop |
65
- | Runs your tests | | ✅ pytest integration |
66
- | Retries on failure | ❌ | ✅ Up to N iterations |
67
- | Works in sandbox | | Isolated workspace |
68
- | Works offline | ❌ | ✅ 100% local via Ollama |
69
- | Costs money | 💸 | ✅ Free forever |
70
-
71
- > **Philosophy:** Execution > Reasoning. Tools > Hallucination. Retrieval > Huge Context. Reliability > Intelligence.
61
+ | **Safety Isolation** | ❌ | ✅ Strict Sandbox Mode |
62
+ | **Recovery** | ❌ | ✅ Git-native + Snapshot Rollback |
63
+ | **Validation** | ❌ | ✅ Empirical Stress Tests |
64
+ | **Transparency** | ❌ | ✅ Visible Failure Taxonomy |
65
+ | **Privacy** | ❌ | ✅ 100% Local (Ollama) |
66
+ | **Costs** | 💸 | ✅ Zero API Costs |
67
+
68
+ > **Philosophy:** Safety > Intelligence. Observability > Reasoning. Retrieval > Huge Context. Reliability > Hype.
72
69
 
73
70
  ---
74
71
 
@@ -141,13 +138,13 @@ devagent run --task "Fix the divide-by-zero bug" --root ./demo_project
141
138
  | `devagent models` | List available Ollama models |
142
139
  | `devagent version` | Show current version |
143
140
 
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.
141
+ #### 🛡️ Reliability & Safety
142
+ DevAgent is built for **production-grade reliability**:
143
+ - **Isolated Sandbox**: Agent works in `sandbox_workspace/`, keeping your source clean until success.
144
+ - **Auto-Snapshot**: Creates a safety restore point before every execution.
145
+ - **Instant Rollback**: Revert agent changes with `devagent rollback`.
146
+ - **Traceability**: Every thought and tool call is logged to `logs/run.json`.
147
+ - **Environment Awareness**: Detects and uses your project's Python environment automatically.
151
148
 
152
149
  #### 🕹️ Interactive Mode
153
150
  Run with `--interactive` (or `-i`) to review colorized diffs before they are applied to your project.
@@ -23,26 +23,23 @@
23
23
 
24
24
  </div>
25
25
 
26
- ## 🤔 Why DevAgent?
26
+ ## 🛡️ Why DevAgent?
27
27
 
28
- Most AI coding tools are **chatbots** they suggest code, you copy-paste, you pray.
28
+ DevAgent is built on a "Safety-First" architecture for **high-integrity developer infrastructure**. Unlike chatbots that guess code, DevAgent is a local autonomous agent that operates within a strictly observed environment.
29
29
 
30
- DevAgent is a **real agent** with a retrieval-first, tool-grounded architecture:
30
+ ### 📊 Empirical Validation
31
+ We don't hide our limitations. DevAgent v3.2.3 has been stress-tested against real-world, "messy" repositories, providing a transparent **[Benchmark Report](docs/benchmarks.md)**.
31
32
 
32
- | | Chatbot | DevAgent |
33
+ | | Other Agents | DevAgent |
33
34
  |---|---|---|
34
- | Searches your codebase | ❌ | ✅ ripgrep + semantic search |
35
- | Retrieves relevant code | ❌ | ✅ FAISS embeddings |
36
- | Plans before coding | ❌ | ✅ Planner layer |
37
- | Generates patches | ❌ | ✅ Unified diffs |
38
- | Reviews its own output | ❌ | ✅ Self-critique loop |
39
- | Runs your tests | | ✅ pytest integration |
40
- | Retries on failure | ❌ | ✅ Up to N iterations |
41
- | Works in sandbox | | Isolated workspace |
42
- | Works offline | ❌ | ✅ 100% local via Ollama |
43
- | Costs money | 💸 | ✅ Free forever |
44
-
45
- > **Philosophy:** Execution > Reasoning. Tools > Hallucination. Retrieval > Huge Context. Reliability > Intelligence.
35
+ | **Safety Isolation** | ❌ | ✅ Strict Sandbox Mode |
36
+ | **Recovery** | ❌ | ✅ Git-native + Snapshot Rollback |
37
+ | **Validation** | ❌ | ✅ Empirical Stress Tests |
38
+ | **Transparency** | ❌ | ✅ Visible Failure Taxonomy |
39
+ | **Privacy** | ❌ | ✅ 100% Local (Ollama) |
40
+ | **Costs** | 💸 | ✅ Zero API Costs |
41
+
42
+ > **Philosophy:** Safety > Intelligence. Observability > Reasoning. Retrieval > Huge Context. Reliability > Hype.
46
43
 
47
44
  ---
48
45
 
@@ -115,13 +112,13 @@ devagent run --task "Fix the divide-by-zero bug" --root ./demo_project
115
112
  | `devagent models` | List available Ollama models |
116
113
  | `devagent version` | Show current version |
117
114
 
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.
115
+ #### 🛡️ Reliability & Safety
116
+ DevAgent is built for **production-grade reliability**:
117
+ - **Isolated Sandbox**: Agent works in `sandbox_workspace/`, keeping your source clean until success.
118
+ - **Auto-Snapshot**: Creates a safety restore point before every execution.
119
+ - **Instant Rollback**: Revert agent changes with `devagent rollback`.
120
+ - **Traceability**: Every thought and tool call is logged to `logs/run.json`.
121
+ - **Environment Awareness**: Detects and uses your project's Python environment automatically.
125
122
 
126
123
  #### 🕹️ Interactive Mode
127
124
  Run with `--interactive` (or `-i`) to review colorized diffs before they are applied to your project.
@@ -0,0 +1 @@
1
+ __version__ = "3.2.3"
@@ -30,6 +30,7 @@ from devagent.app.patcher import generate_diff, apply_patch, format_diff_summary
30
30
  from devagent.app.memory import WorkingMemory, chunk_project, SemanticIndex
31
31
  from devagent.tools.search import search_code
32
32
  from devagent.tools.file_ops import read_file, write_file, list_files
33
+ from devagent.tools.file_map import get_file_map
33
34
  from devagent.tools.test_runner import run_tests
34
35
  from devagent.tools.linter import lint_code
35
36
  from devagent.tools.git_tools import git_diff, git_status
@@ -47,7 +48,6 @@ You are a coding agent. Your task:
47
48
 
48
49
  Project root: {project_root}
49
50
  Current step: {step}/{max_steps}
50
- Previous attempts: {attempts}
51
51
 
52
52
  {plan_context}
53
53
 
@@ -59,9 +59,10 @@ Previous attempts: {attempts}
59
59
 
60
60
  Decide the SINGLE next action. Choose ONE:
61
61
  - list_files: <relative_path>
62
+ - get_file_map: <relative_path>
62
63
  - search_code: <keyword>
63
64
  - semantic_search: <query>
64
- - read_file: <relative_path>
65
+ - read_file: <relative_path>[:L<start>-<end>]
65
66
  - write_file: <relative_path>
66
67
  - surgical_patch: <file> | <SEARCH> | <REPLACE>
67
68
  - run_tests: <optional_path>
@@ -69,12 +70,11 @@ Decide the SINGLE next action. Choose ONE:
69
70
  - git_diff
70
71
 
71
72
  STRATEGY:
72
- 1. START by using 'run_tests' to identify the exact failure location.
73
- 2. USE 'read_file' on the failing file before attempting a fix.
74
- 3. PREFER 'surgical_patch' for logic fixes. Format: file.py | <SEARCH> | <REPLACE>
75
- 4. USE 'write_file' ONLY for creating brand new files.
76
- 5. ALWAYS use full relative paths (e.g. benchmarks/math/calc.py).
77
- 6. If tests pass but system errors remain, verify the file path is correct.
73
+ 1. START by using 'run_tests' to identify the failure.
74
+ 2. For large files (>50 lines), use 'get_file_map' FIRST to see the structure.
75
+ 3. USE 'read_file' with line ranges (e.g. file.py:L10-L50) for targeted reading.
76
+ 4. USE 'surgical_patch' for logic fixes. Format: file.py | <SEARCH> | <REPLACE>
77
+ 5. ALWAYS use full relative paths.
78
78
 
79
79
  Reply in this EXACT format (two lines only):
80
80
  THOUGHT: <your reasoning>
@@ -92,21 +92,11 @@ CURRENT CODE:
92
92
 
93
93
  {retrieval_context}
94
94
 
95
- TASK: {task}
96
- FILE: {file_path}
97
-
98
- {error_context}
99
-
100
- {retrieval_context}
101
-
102
- CURRENT CODE:
103
- {file_content}
104
-
105
95
  Fix the bug. Output ONLY the COMPLETE Python code.
106
96
  """
107
97
 
108
98
  EXTRACT_ACTION_PATTERN = re.compile(
109
- r"ACTION:\s*(search_code|semantic_search|read_file|write_file|surgical_patch|run_tests|lint_code|list_files|git_diff)\s*:?\s*(.*)",
99
+ r"ACTION:\s*(get_file_map|search_code|semantic_search|read_file|write_file|surgical_patch|run_tests|lint_code|list_files|git_diff)\s*:?\s*(.*)",
110
100
  re.IGNORECASE,
111
101
  )
112
102
 
@@ -280,6 +270,12 @@ class Agent:
280
270
  observation = self._execute_action(action_name, action_arg)
281
271
  self.state.last_observation = observation
282
272
  self.state.observations.append(observation[:2000])
273
+
274
+ self.state.explanations.append({
275
+ "type": "action",
276
+ "action": f"{action_name}: {action_arg}",
277
+ "reason": thought
278
+ })
283
279
 
284
280
  # STEP 3 — GENERATE FIX (if we have a file in context)
285
281
  code_fix = ""
@@ -290,8 +286,12 @@ class Agent:
290
286
  code_fix = self._generate_fix()
291
287
  self.state.last_code_fix = code_fix
292
288
 
293
- # STEP 4 — SELF-REVIEW
289
+ # STEP 4 — SELF-REVIEW (with Synthetic Grounding)
294
290
  if code_fix:
291
+ # Add a "Dry Run" lint check to the review context
292
+ _, lint_output = lint_code(self.state.current_file)
293
+ self.state.last_observation += f"\n[LINT] {lint_output}"
294
+
295
295
  code_fix, review_text = self._self_review(code_fix)
296
296
  self.state.last_review = review_text
297
297
 
@@ -460,6 +460,9 @@ class Agent:
460
460
  self._extract_file_from_search(result)
461
461
  return result
462
462
 
463
+ elif action_name == "get_file_map":
464
+ return get_file_map(action_arg, root)
465
+
463
466
  elif action_name == "semantic_search":
464
467
  result = semantic_search(action_arg, root)
465
468
  # Try to extract file from results
@@ -555,9 +558,21 @@ class Agent:
555
558
  print(f" [REVIEW] #{revision + 1}: {review_text[:100]}")
556
559
 
557
560
  if approved:
561
+ self.state.explanations.append({
562
+ "type": "review",
563
+ "file": self.state.current_file,
564
+ "reason": review_text,
565
+ "status": "APPROVED"
566
+ })
558
567
  return code_fix, review_text
559
568
 
560
569
  self.metrics.patch_rejections += 1
570
+ self.state.explanations.append({
571
+ "type": "review",
572
+ "file": self.state.current_file,
573
+ "reason": review_text,
574
+ "status": "REJECTED"
575
+ })
561
576
  print(f" [REVISE] Revising code (attempt {revision + 1})...")
562
577
  code_fix = revise_code(code_fix, review_text, self.state.task)
563
578
  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."""
@@ -142,7 +142,21 @@ def cmd_run(args):
142
142
  if final_state.confidence_reasons:
143
143
  console.print("\n[bold]Confidence Breakdown:[/bold]")
144
144
  for reason in final_state.confidence_reasons:
145
- console.print(f" [dim] {reason}[/dim]")
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']}")
146
160
 
147
161
  # Sandbox apply / Dry Run
148
162
  if sandbox and sandbox.is_active:
@@ -273,6 +287,7 @@ def main():
273
287
  run_parser.add_argument("--auto-push", action="store_true", help="Auto-push after commit")
274
288
  run_parser.add_argument("--interactive", "-i", action="store_true", help="Review changes before applying")
275
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")
276
291
  run_parser.add_argument("--verbose", action="store_true", help="Verbose output")
277
292
 
278
293
  # Command: benchmark
@@ -0,0 +1,21 @@
1
+ """
2
+ File Map Tool — Provides a structural overview of a file using AST.
3
+ """
4
+
5
+ from __future__ import annotations
6
+ import os
7
+ from devagent.utils.ast_utils import get_file_structure, format_structure_summary
8
+
9
+ def get_file_map(file_path: str, root_dir: str = ".") -> str:
10
+ """Return a hierarchical map of functions and classes in the file."""
11
+ full_path = os.path.join(root_dir, file_path)
12
+ if not os.path.exists(full_path):
13
+ return f"Error: File {file_path} not found."
14
+
15
+ symbols = get_file_structure(full_path)
16
+ if not symbols:
17
+ return f"Could not parse structure for {file_path} (possibly empty or non-python)."
18
+
19
+ summary = format_structure_summary(symbols)
20
+ header = f"### Structure of {file_path} ###\n"
21
+ return header + summary + "\n\nUse read_file with specific line ranges to see the implementation."
@@ -10,16 +10,36 @@ from pathlib import Path
10
10
 
11
11
  def read_file(path: str) -> str:
12
12
  """Read a file and return its contents.
13
-
13
+
14
+ Supports range: path/to/file.py:L10-L50
14
15
  Returns an error string on failure (never raises).
15
16
  """
16
17
  try:
18
+ # Check for line range
19
+ line_range = None
20
+ if ":L" in path:
21
+ parts = path.split(":L")
22
+ path = parts[0]
23
+ line_range = parts[1]
24
+
17
25
  p = Path(path)
18
26
  if not p.exists():
19
27
  return f"[ERROR] File not found: {path}"
20
28
  if not p.is_file():
21
29
  return f"[ERROR] Not a file: {path}"
22
- content = p.read_text(encoding="utf-8", errors="replace")
30
+
31
+ lines = p.read_text(encoding="utf-8", errors="replace").splitlines()
32
+
33
+ if line_range:
34
+ try:
35
+ start, end = map(int, line_range.split("-"))
36
+ lines = lines[start-1:end]
37
+ header = f"### {path} (Lines {start}-{end}) ###\n"
38
+ return header + "\n".join(lines)
39
+ except ValueError:
40
+ return f"[ERROR] Invalid line range: {line_range}"
41
+
42
+ content = "\n".join(lines)
23
43
  return content[:10000] # cap to protect LLM context
24
44
  except Exception as exc: # noqa: BLE001
25
45
  return f"[ERROR] Could not read {path}: {exc}"
@@ -0,0 +1,83 @@
1
+ """
2
+ AST Utilities for DevAgent — Hierarchical mapping and structural analysis.
3
+
4
+ Helps the agent understand file structure without reading the whole file.
5
+ """
6
+
7
+ import ast
8
+ from dataclasses import dataclass
9
+ from typing import List, Optional
10
+
11
+ @dataclass
12
+ class CodeSymbol:
13
+ name: str
14
+ type: str # 'function', 'class', 'method'
15
+ start_line: int
16
+ end_line: int
17
+ docstring: Optional[str] = None
18
+ children: List['CodeSymbol'] = None
19
+
20
+ def __post_init__(self):
21
+ if self.children is None:
22
+ self.children = []
23
+
24
+ def get_file_structure(file_path: str) -> List[CodeSymbol]:
25
+ """Parse a Python file and return a hierarchical list of symbols."""
26
+ try:
27
+ with open(file_path, "r", encoding="utf-8", errors="replace") as f:
28
+ content = f.read()
29
+
30
+ tree = ast.parse(content)
31
+ symbols = []
32
+
33
+ for node in ast.iter_child_nodes(tree):
34
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
35
+ symbol = _parse_node(node)
36
+ if symbol:
37
+ symbols.append(symbol)
38
+
39
+ return symbols
40
+ except Exception:
41
+ return []
42
+
43
+ def _parse_node(node: ast.AST) -> Optional[CodeSymbol]:
44
+ """Recursively parse an AST node into a CodeSymbol."""
45
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
46
+ symbol = CodeSymbol(
47
+ name=node.name,
48
+ type="function",
49
+ start_line=node.lineno,
50
+ end_line=getattr(node, "end_lineno", node.lineno),
51
+ docstring=ast.get_docstring(node)
52
+ )
53
+ return symbol
54
+
55
+ elif isinstance(node, ast.ClassDef):
56
+ symbol = CodeSymbol(
57
+ name=node.name,
58
+ type="class",
59
+ start_line=node.lineno,
60
+ end_line=getattr(node, "end_lineno", node.lineno),
61
+ docstring=ast.get_docstring(node)
62
+ )
63
+ # Parse methods
64
+ for child in node.body:
65
+ if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)):
66
+ method = _parse_node(child)
67
+ if method:
68
+ method.type = "method"
69
+ symbol.children.append(method)
70
+ return symbol
71
+
72
+ return None
73
+
74
+ def format_structure_summary(symbols: List[CodeSymbol], indent: int = 0) -> str:
75
+ """Format symbols into a readable tree summary."""
76
+ lines = []
77
+ for s in symbols:
78
+ prefix = " " * indent
79
+ line = f"{prefix}- {s.type.capitalize()}: {s.name} (L{s.start_line}-{s.end_line})"
80
+ lines.append(line)
81
+ if s.children:
82
+ lines.append(format_structure_summary(s.children, indent + 1))
83
+ return "\n".join(lines)
@@ -54,6 +54,7 @@ class AgentConfig:
54
54
  # ── Feature flags ──
55
55
  sandbox: bool = True
56
56
  dry_run: bool = False
57
+ explain: bool = False
57
58
  auto_commit: bool = False
58
59
  auto_push: bool = False # DISABLED by default — safety first
59
60
  benchmark: bool = False
@@ -100,6 +101,7 @@ class AgentConfig:
100
101
  verbose=getattr(args, "verbose", False),
101
102
  sandbox=getattr(args, "sandbox", True),
102
103
  dry_run=getattr(args, "dry_run", False),
104
+ explain=getattr(args, "explain", False),
103
105
  auto_commit=getattr(args, "auto_commit", False),
104
106
  auto_push=getattr(args, "auto_push", False),
105
107
  benchmark=getattr(args, "benchmark", False),
@@ -5,6 +5,7 @@ Safety Manager — handles snapshots and rollbacks.
5
5
  import os
6
6
  import shutil
7
7
  import time
8
+ import subprocess
8
9
  from rich.console import Console
9
10
  from devagent.tools.git_tools import is_git_repo
10
11
 
@@ -13,7 +14,7 @@ console = Console()
13
14
  class SafetyManager:
14
15
  def __init__(self, project_root: str):
15
16
  self.root = os.path.abspath(project_root)
16
- self.snapshot_dir = os.path.join(self.root, ".devagent", "snapshots")
17
+ self.snapshot_dir = os.path.join(self.root, ".devagent_backups")
17
18
 
18
19
  def create_snapshot(self, task_id: str = "latest") -> str:
19
20
  """Create a safety snapshot of the current project state."""
@@ -40,13 +41,29 @@ class SafetyManager:
40
41
  os.makedirs(os.path.dirname(target_file), exist_ok=True)
41
42
  shutil.copy2(src_file, target_file)
42
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
+
43
54
  return dest
44
55
 
45
56
  def rollback(self, snapshot_id: str = "latest") -> bool:
46
57
  """Rollback the project to a previous snapshot."""
47
58
  if is_git_repo(self.root):
48
- console.print("[bold yellow][SAFETY][/bold yellow] This is a Git repository. Use [bold cyan]git checkout .[/bold cyan] to rollback.")
49
- return False
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
50
67
 
51
68
  # Find latest snapshot if not specified
52
69
  if not os.path.exists(self.snapshot_dir):
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devagent-cli
3
- Version: 3.2.2
4
- Summary: Professional Local autonomous coding agent powered by Ollama
3
+ Version: 3.3.0
4
+ Summary: A local autonomous coding agent CLI powered by Ollama.
5
5
  Author: Vedant Jadhav
6
6
  License: MIT
7
7
  Keywords: ai,agent,coding,ollama,local,devagent,devagent-cli
@@ -49,26 +49,23 @@ Dynamic: license-file
49
49
 
50
50
  </div>
51
51
 
52
- ## 🤔 Why DevAgent?
52
+ ## 🛡️ Why DevAgent?
53
53
 
54
- Most AI coding tools are **chatbots** they suggest code, you copy-paste, you pray.
54
+ DevAgent is built on a "Safety-First" architecture for **high-integrity developer infrastructure**. Unlike chatbots that guess code, DevAgent is a local autonomous agent that operates within a strictly observed environment.
55
55
 
56
- DevAgent is a **real agent** with a retrieval-first, tool-grounded architecture:
56
+ ### 📊 Empirical Validation
57
+ We don't hide our limitations. DevAgent v3.2.3 has been stress-tested against real-world, "messy" repositories, providing a transparent **[Benchmark Report](docs/benchmarks.md)**.
57
58
 
58
- | | Chatbot | DevAgent |
59
+ | | Other Agents | DevAgent |
59
60
  |---|---|---|
60
- | Searches your codebase | ❌ | ✅ ripgrep + semantic search |
61
- | Retrieves relevant code | ❌ | ✅ FAISS embeddings |
62
- | Plans before coding | ❌ | ✅ Planner layer |
63
- | Generates patches | ❌ | ✅ Unified diffs |
64
- | Reviews its own output | ❌ | ✅ Self-critique loop |
65
- | Runs your tests | | ✅ pytest integration |
66
- | Retries on failure | ❌ | ✅ Up to N iterations |
67
- | Works in sandbox | | Isolated workspace |
68
- | Works offline | ❌ | ✅ 100% local via Ollama |
69
- | Costs money | 💸 | ✅ Free forever |
70
-
71
- > **Philosophy:** Execution > Reasoning. Tools > Hallucination. Retrieval > Huge Context. Reliability > Intelligence.
61
+ | **Safety Isolation** | ❌ | ✅ Strict Sandbox Mode |
62
+ | **Recovery** | ❌ | ✅ Git-native + Snapshot Rollback |
63
+ | **Validation** | ❌ | ✅ Empirical Stress Tests |
64
+ | **Transparency** | ❌ | ✅ Visible Failure Taxonomy |
65
+ | **Privacy** | ❌ | ✅ 100% Local (Ollama) |
66
+ | **Costs** | 💸 | ✅ Zero API Costs |
67
+
68
+ > **Philosophy:** Safety > Intelligence. Observability > Reasoning. Retrieval > Huge Context. Reliability > Hype.
72
69
 
73
70
  ---
74
71
 
@@ -141,13 +138,13 @@ devagent run --task "Fix the divide-by-zero bug" --root ./demo_project
141
138
  | `devagent models` | List available Ollama models |
142
139
  | `devagent version` | Show current version |
143
140
 
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.
141
+ #### 🛡️ Reliability & Safety
142
+ DevAgent is built for **production-grade reliability**:
143
+ - **Isolated Sandbox**: Agent works in `sandbox_workspace/`, keeping your source clean until success.
144
+ - **Auto-Snapshot**: Creates a safety restore point before every execution.
145
+ - **Instant Rollback**: Revert agent changes with `devagent rollback`.
146
+ - **Traceability**: Every thought and tool call is logged to `logs/run.json`.
147
+ - **Environment Awareness**: Detects and uses your project's Python environment automatically.
151
148
 
152
149
  #### 🕹️ Interactive Mode
153
150
  Run with `--interactive` (or `-i`) to review colorized diffs before they are applied to your project.
@@ -14,6 +14,7 @@ devagent/app/sandbox.py
14
14
  devagent/app/state.py
15
15
  devagent/tools/__init__.py
16
16
  devagent/tools/benchmark_runner.py
17
+ devagent/tools/file_map.py
17
18
  devagent/tools/file_ops.py
18
19
  devagent/tools/git_tools.py
19
20
  devagent/tools/linter.py
@@ -22,6 +23,7 @@ devagent/tools/semantic_search.py
22
23
  devagent/tools/surgical_patcher.py
23
24
  devagent/tools/test_runner.py
24
25
  devagent/utils/__init__.py
26
+ devagent/utils/ast_utils.py
25
27
  devagent/utils/config.py
26
28
  devagent/utils/logger.py
27
29
  devagent/utils/metrics.py
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "devagent-cli"
7
- version = "3.2.2"
8
- description = "Professional Local autonomous coding agent powered by Ollama"
7
+ version = "3.3.0"
8
+ description = "A local autonomous coding agent CLI powered by Ollama."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
11
11
  license = {text = "MIT"}
@@ -1 +0,0 @@
1
- __version__ = "3.2.1"
File without changes
File without changes