alpiecode 9.0.2__tar.gz → 9.0.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.
- {alpiecode-9.0.2 → alpiecode-9.0.3}/PKG-INFO +1 -1
- {alpiecode-9.0.2 → alpiecode-9.0.3}/pyproject.toml +1 -1
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode.egg-info/PKG-INFO +1 -1
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/__init__.py +1 -1
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/backends/openai_backend.py +1 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/discovery.py +16 -1
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/rephraser.py +7 -2
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/tools.py +65 -17
- {alpiecode-9.0.2 → alpiecode-9.0.3}/README.md +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/setup.cfg +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode/__init__.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode.egg-info/SOURCES.txt +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode.egg-info/dependency_links.txt +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode.egg-info/entry_points.txt +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode.egg-info/requires.txt +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode.egg-info/top_level.txt +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/agent.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/backends/__init__.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/backends/base.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/backends/local_backend.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/cache.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/cli.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/client.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/compaction.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/config.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/context.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/doctor.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/executor.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/extension/alpiecode.vsix +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/git_ops.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/github.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/guardian.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/guardrails.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/ipython_ext.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/local_model.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/media.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/memory.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/orchestrator.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/progress.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/prompt.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/server.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/session.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/updater.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/vscode_installer.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/test/test_phase1.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/test/test_phase2.py +0 -0
- {alpiecode-9.0.2 → alpiecode-9.0.3}/test/test_phase3.py +0 -0
|
@@ -16,7 +16,7 @@ Supports:
|
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
from typing import Optional, Any, Dict
|
|
18
18
|
|
|
19
|
-
__version__ = "9.0.
|
|
19
|
+
__version__ = "9.0.3"
|
|
20
20
|
|
|
21
21
|
def run(task: str, workdir: str = ".", reasoning_level: str = "thinking", **kwargs):
|
|
22
22
|
"""Run an autonomous coding task programmatically."""
|
|
@@ -442,11 +442,26 @@ def compute_complexity(intent: str, repo_info: dict, task: str) -> str:
|
|
|
442
442
|
if intent == "qa":
|
|
443
443
|
return "qa"
|
|
444
444
|
|
|
445
|
-
task_lower = task.lower()
|
|
445
|
+
task_lower = task.lower().strip()
|
|
446
446
|
file_count = repo_info.get("file_count", 0)
|
|
447
447
|
frameworks = repo_info.get("frameworks", [])
|
|
448
448
|
project_type = repo_info.get("project_type", "empty")
|
|
449
449
|
|
|
450
|
+
# Standalone algorithms / simple scripts: keep 'low' complexity so they
|
|
451
|
+
# complete in 1-2 turns without creating multi-file architectures or plans.
|
|
452
|
+
standalone_keywords = [
|
|
453
|
+
"palindrome", "fibonacci", "quicksort", "bubblesort",
|
|
454
|
+
"binary search", "factorial", "hello world", "calculator",
|
|
455
|
+
"is_palindrome", "check palindrome", "reverse string",
|
|
456
|
+
"prime number", "anagram", "todo script", "simple script",
|
|
457
|
+
]
|
|
458
|
+
project_terms = [
|
|
459
|
+
"this project", "this repo", "our repo", "our project", "existing",
|
|
460
|
+
"endpoint", "route", "database", "migration", "service", "component",
|
|
461
|
+
]
|
|
462
|
+
if any(kw in task_lower for kw in standalone_keywords) and not any(pt in task_lower for pt in project_terms):
|
|
463
|
+
return "low"
|
|
464
|
+
|
|
450
465
|
# -- HIGH: explicitly complex multi-component tasks --
|
|
451
466
|
high_keywords = [
|
|
452
467
|
"full stack", "fullstack", "full-stack",
|
|
@@ -18,7 +18,10 @@ Your job is to transform the user's raw prompt into a clear, solid, and unambigu
|
|
|
18
18
|
Guidelines:
|
|
19
19
|
1. If the prompt is a simple Q&A, greeting, or direct conceptual question (e.g. "what is X?", "explain Y", "hi", "how does Z work"):
|
|
20
20
|
- Keep it clean, direct, and concise. Do NOT add unnecessary software development boilerplate.
|
|
21
|
-
2. If the prompt is a
|
|
21
|
+
2. If the prompt is a standalone algorithm, utility, or single-file script (e.g. "check palindrome", "fibonacci", "calculator", "sort"):
|
|
22
|
+
- Keep the specification strictly focused on a SINGLE runnable executable script (e.g. `palindrome.py`).
|
|
23
|
+
- Do NOT turn it into a multi-file architecture, microservice, or web framework (do NOT add FastAPI, Flask, Pydantic, Docker, or separate schemas/models/utils unless the user explicitly requested a web service).
|
|
24
|
+
3. If the prompt is a coding, debugging, refactoring, testing, or feature creation task:
|
|
22
25
|
- Expand it into a solid, structured engineering prompt with:
|
|
23
26
|
* Objective: Explicit statement of what needs to be created, modified, or fixed
|
|
24
27
|
* Scope & Architecture: Target files, patterns, and module structure
|
|
@@ -106,7 +109,9 @@ class PromptRephraser:
|
|
|
106
109
|
try:
|
|
107
110
|
# Inject project context if available so rephrased spec is project-aware
|
|
108
111
|
project_hint = ""
|
|
109
|
-
|
|
112
|
+
standalone_kws = ["palindrome", "fibonacci", "quicksort", "bubblesort", "factorial", "hello world", "calculator", "reverse string"]
|
|
113
|
+
is_standalone = any(kw in task.lower() for kw in standalone_kws)
|
|
114
|
+
if task_context and not is_standalone:
|
|
110
115
|
_fw = ", ".join(getattr(task_context, "frameworks", []) or []) or "none"
|
|
111
116
|
_pt = getattr(task_context, "project_type", "unknown")
|
|
112
117
|
_fc = getattr(task_context, "file_count", 0)
|
|
@@ -853,34 +853,82 @@ def _clone_repo(workdir: Path, repo_url: str, branch: str = None) -> str:
|
|
|
853
853
|
# ── Dispatch factory ──────────────────────────────────────────────────
|
|
854
854
|
|
|
855
855
|
def make_dispatch(workdir: Path):
|
|
856
|
-
"""Bind tool implementations to a specific working directory."""
|
|
856
|
+
"""Bind tool implementations to a specific working directory with resilient parameter extraction."""
|
|
857
|
+
|
|
858
|
+
def _safe_bash(a: dict) -> str:
|
|
859
|
+
cmd = a.get("command") or a.get("cmd") or a.get("script") or a.get("shell_command") or ""
|
|
860
|
+
if not str(cmd).strip():
|
|
861
|
+
return "error: 'command' parameter is required and cannot be empty"
|
|
862
|
+
return _bash(workdir, str(cmd))
|
|
863
|
+
|
|
864
|
+
def _safe_read_file(a: dict) -> str:
|
|
865
|
+
path = a.get("path") or a.get("file") or a.get("filepath") or a.get("filename") or ""
|
|
866
|
+
if not str(path).strip():
|
|
867
|
+
return "error: 'path' parameter is required for read_file"
|
|
868
|
+
return _read_file(workdir, str(path), a.get("start_line"), a.get("end_line"))
|
|
869
|
+
|
|
870
|
+
def _safe_write_file(a: dict) -> str:
|
|
871
|
+
path = a.get("path") or a.get("file") or a.get("filepath") or a.get("filename") or ""
|
|
872
|
+
if not str(path).strip():
|
|
873
|
+
return "error: 'path' parameter is required for write_file"
|
|
874
|
+
content = None
|
|
875
|
+
for k in ("content", "code", "text", "body", "file_content", "data", "contents"):
|
|
876
|
+
if k in a and a[k] is not None:
|
|
877
|
+
content = str(a[k])
|
|
878
|
+
break
|
|
879
|
+
if content is None:
|
|
880
|
+
return f"error: 'content' parameter is required for write_file to '{path}'. Please provide the file content."
|
|
881
|
+
return _write_file(workdir, str(path), content)
|
|
882
|
+
|
|
883
|
+
def _safe_edit_file(a: dict) -> str:
|
|
884
|
+
path = a.get("path") or a.get("file") or a.get("filepath") or a.get("filename") or ""
|
|
885
|
+
if not str(path).strip():
|
|
886
|
+
return "error: 'path' parameter is required for edit_file"
|
|
887
|
+
old_str = a.get("old_str") if a.get("old_str") is not None else a.get("old_string") if a.get("old_string") is not None else a.get("target")
|
|
888
|
+
if old_str is None:
|
|
889
|
+
return "error: 'old_str' parameter is required for edit_file (the exact string to replace)"
|
|
890
|
+
new_str = a.get("new_str") if a.get("new_str") is not None else a.get("new_string") if a.get("new_string") is not None else a.get("replacement") if a.get("replacement") is not None else a.get("code") if a.get("code") is not None else ""
|
|
891
|
+
return _edit_file(workdir, str(path), str(old_str), str(new_str))
|
|
892
|
+
|
|
893
|
+
def _safe_file_search(a: dict) -> str:
|
|
894
|
+
pattern = a.get("pattern") or a.get("query") or a.get("regex") or ""
|
|
895
|
+
if not str(pattern).strip():
|
|
896
|
+
return "error: 'pattern' parameter is required for file_search"
|
|
897
|
+
return _file_search(workdir, str(pattern), a.get("path", "."),
|
|
898
|
+
a.get("include"), a.get("case_insensitive", False))
|
|
899
|
+
|
|
900
|
+
def _safe_update_plan(a: dict) -> str:
|
|
901
|
+
plan = a.get("plan") or a.get("content") or a.get("steps") or a.get("summary") or ""
|
|
902
|
+
if not str(plan).strip():
|
|
903
|
+
return "error: 'plan' parameter is required for update_plan"
|
|
904
|
+
return _update_plan(workdir, str(plan))
|
|
905
|
+
|
|
857
906
|
return {
|
|
858
|
-
"bash":
|
|
859
|
-
"read_file":
|
|
860
|
-
"write_file":
|
|
861
|
-
"edit_file":
|
|
862
|
-
"list_files": lambda a: _list_files(workdir, a.get("path"
|
|
863
|
-
"file_search":
|
|
864
|
-
|
|
865
|
-
"
|
|
866
|
-
"
|
|
867
|
-
"
|
|
868
|
-
"
|
|
869
|
-
"
|
|
870
|
-
"view_image": lambda a: _view_image(workdir, a["path"]),
|
|
907
|
+
"bash": _safe_bash,
|
|
908
|
+
"read_file": _safe_read_file,
|
|
909
|
+
"write_file": _safe_write_file,
|
|
910
|
+
"edit_file": _safe_edit_file,
|
|
911
|
+
"list_files": lambda a: _list_files(workdir, a.get("path") or a.get("dir") or ".", a.get("max_depth", 4)),
|
|
912
|
+
"file_search": _safe_file_search,
|
|
913
|
+
"apply_patch": lambda a: _apply_patch(workdir, a.get("path") or a.get("file", ""), a.get("patch") or a.get("diff", "")),
|
|
914
|
+
"web_search": lambda a: _web_search(a.get("query") or a.get("q", ""), a.get("num_results", 5)),
|
|
915
|
+
"fetch_url": lambda a: _fetch_url(a.get("url") or a.get("uri", "")),
|
|
916
|
+
"request_user_input": lambda a: _request_user_input(a.get("question") or a.get("prompt", "")),
|
|
917
|
+
"update_plan": _safe_update_plan,
|
|
918
|
+
"view_image": lambda a: _view_image(workdir, a.get("path") or a.get("file", "")),
|
|
871
919
|
"github_issues": lambda a: _github_issues(
|
|
872
|
-
a
|
|
920
|
+
a.get("owner", ""), a.get("repo", ""),
|
|
873
921
|
issue_number=a.get("issue_number"),
|
|
874
922
|
state=a.get("state", "open"),
|
|
875
923
|
max_results=a.get("max_results", 10),
|
|
876
924
|
),
|
|
877
925
|
"github_browse": lambda a: _github_browse(
|
|
878
|
-
a
|
|
926
|
+
a.get("owner", ""), a.get("repo", ""),
|
|
879
927
|
path=a.get("path", ""),
|
|
880
928
|
info_only=a.get("info_only", False),
|
|
881
929
|
),
|
|
882
930
|
"clone_repo": lambda a: _clone_repo(
|
|
883
|
-
workdir, a
|
|
931
|
+
workdir, a.get("repo_url") or a.get("url", ""),
|
|
884
932
|
branch=a.get("branch"),
|
|
885
933
|
),
|
|
886
934
|
}
|
|
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
|
|
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
|