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.
Files changed (47) hide show
  1. {alpiecode-9.0.2 → alpiecode-9.0.3}/PKG-INFO +1 -1
  2. {alpiecode-9.0.2 → alpiecode-9.0.3}/pyproject.toml +1 -1
  3. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode.egg-info/PKG-INFO +1 -1
  4. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/__init__.py +1 -1
  5. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/backends/openai_backend.py +1 -0
  6. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/discovery.py +16 -1
  7. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/rephraser.py +7 -2
  8. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/tools.py +65 -17
  9. {alpiecode-9.0.2 → alpiecode-9.0.3}/README.md +0 -0
  10. {alpiecode-9.0.2 → alpiecode-9.0.3}/setup.cfg +0 -0
  11. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode/__init__.py +0 -0
  12. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode.egg-info/SOURCES.txt +0 -0
  13. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode.egg-info/dependency_links.txt +0 -0
  14. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode.egg-info/entry_points.txt +0 -0
  15. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode.egg-info/requires.txt +0 -0
  16. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/alpiecode.egg-info/top_level.txt +0 -0
  17. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/agent.py +0 -0
  18. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/backends/__init__.py +0 -0
  19. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/backends/base.py +0 -0
  20. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/backends/local_backend.py +0 -0
  21. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/cache.py +0 -0
  22. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/cli.py +0 -0
  23. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/client.py +0 -0
  24. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/compaction.py +0 -0
  25. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/config.py +0 -0
  26. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/context.py +0 -0
  27. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/doctor.py +0 -0
  28. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/executor.py +0 -0
  29. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/extension/alpiecode.vsix +0 -0
  30. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/git_ops.py +0 -0
  31. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/github.py +0 -0
  32. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/guardian.py +0 -0
  33. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/guardrails.py +0 -0
  34. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/ipython_ext.py +0 -0
  35. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/local_model.py +0 -0
  36. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/media.py +0 -0
  37. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/memory.py +0 -0
  38. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/orchestrator.py +0 -0
  39. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/progress.py +0 -0
  40. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/prompt.py +0 -0
  41. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/server.py +0 -0
  42. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/session.py +0 -0
  43. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/updater.py +0 -0
  44. {alpiecode-9.0.2 → alpiecode-9.0.3}/src/codeagent/vscode_installer.py +0 -0
  45. {alpiecode-9.0.2 → alpiecode-9.0.3}/test/test_phase1.py +0 -0
  46. {alpiecode-9.0.2 → alpiecode-9.0.3}/test/test_phase2.py +0 -0
  47. {alpiecode-9.0.2 → alpiecode-9.0.3}/test/test_phase3.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpiecode
3
- Version: 9.0.2
3
+ Version: 9.0.3
4
4
  Summary: AlpieCode — Autonomous AI Coding Agent CLI powered by local 169Pi GGUF VLM
5
5
  Requires-Python: >=3.9
6
6
  Requires-Dist: huggingface_hub>=0.20.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "alpiecode"
7
- version = "9.0.2"
7
+ version = "9.0.3"
8
8
  description = "AlpieCode — Autonomous AI Coding Agent CLI powered by local 169Pi GGUF VLM"
9
9
  requires-python = ">=3.9"
10
10
  dependencies = [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpiecode
3
- Version: 9.0.2
3
+ Version: 9.0.3
4
4
  Summary: AlpieCode — Autonomous AI Coding Agent CLI powered by local 169Pi GGUF VLM
5
5
  Requires-Python: >=3.9
6
6
  Requires-Dist: huggingface_hub>=0.20.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.2"
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."""
@@ -196,6 +196,7 @@ class OpenAIBackend:
196
196
  "max_tokens": m_tokens,
197
197
  "extra_body": {"chat_template_kwargs": {"enable_thinking": enable_thinking}},
198
198
  "stream": True,
199
+ "timeout": 45.0,
199
200
  }
200
201
  if tools:
201
202
  params["tools"] = tools
@@ -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 coding, debugging, refactoring, testing, or feature creation task:
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
- if task_context:
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": lambda a: _bash(workdir, a["command"]),
859
- "read_file": lambda a: _read_file(workdir, a["path"], a.get("start_line"), a.get("end_line")),
860
- "write_file": lambda a: _write_file(workdir, a["path"], a["content"]),
861
- "edit_file": lambda a: _edit_file(workdir, a["path"], a["old_str"], a["new_str"]),
862
- "list_files": lambda a: _list_files(workdir, a.get("path", "."), a.get("max_depth", 4)),
863
- "file_search": lambda a: _file_search(workdir, a["pattern"], a.get("path", "."),
864
- a.get("include"), a.get("case_insensitive", False)),
865
- "apply_patch": lambda a: _apply_patch(workdir, a["path"], a["patch"]),
866
- "web_search": lambda a: _web_search(a["query"], a.get("num_results", 5)),
867
- "fetch_url": lambda a: _fetch_url(a["url"]),
868
- "request_user_input": lambda a: _request_user_input(a["question"]),
869
- "update_plan": lambda a: _update_plan(workdir, a["plan"]),
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["owner"], a["repo"],
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["owner"], a["repo"],
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["repo_url"],
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