opalacoder 0.1.0__tar.gz → 0.1.1__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.
- opalacoder-0.1.1/.gitignore +145 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/PKG-INFO +1 -1
- {opalacoder-0.1.0 → opalacoder-0.1.1}/agents.yaml +1 -1
- opalacoder-0.1.1/direct_gemini_test.py +27 -0
- opalacoder-0.1.1/logotipo.png +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/agents.py +11 -5
- opalacoder-0.1.1/opalacoder/archival.py +77 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/cli.py +15 -3
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/cli_commands.py +2 -2
- opalacoder-0.1.1/opalacoder/memgpt.py +411 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/orchestrator.py +92 -41
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/project.py +30 -5
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/tools.py +53 -5
- {opalacoder-0.1.0 → opalacoder-0.1.1}/pyproject.toml +1 -1
- opalacoder-0.1.1/pytest.ini +7 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/requirements.txt +2 -1
- opalacoder-0.1.1/test_complexity.py +1 -0
- opalacoder-0.1.1/test_gemini.py +20 -0
- opalacoder-0.1.1/test_gemini_sequences.py +52 -0
- opalacoder-0.1.1/tests/test_executor_context_extraction.py +1 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/tests/test_intent_classifier.py +7 -1
- opalacoder-0.1.1/tests/test_new_features.py +1 -0
- opalacoder-0.1.1/tests/test_pipeline_integration.py +1 -0
- opalacoder-0.1.1/tests/test_planner_flow.py +1 -0
- opalacoder-0.1.1/tests/test_project_dir_isolation.py +1 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/tests/test_refinement_loop.py +40 -44
- opalacoder-0.1.0/.gitignore +0 -8
- opalacoder-0.1.0/test_complexity.py +0 -13
- opalacoder-0.1.0/tests/test_executor_context_extraction.py +0 -119
- opalacoder-0.1.0/tests/test_new_features.py +0 -262
- opalacoder-0.1.0/tests/test_pipeline_integration.py +0 -225
- opalacoder-0.1.0/tests/test_planner_flow.py +0 -146
- opalacoder-0.1.0/tests/test_project_dir_isolation.py +0 -248
- {opalacoder-0.1.0 → opalacoder-0.1.1}/.github/workflows/publish.yml +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/AGENT.md +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/ALGS.md +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/ARCH_SUMMARY.md +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/CLAUDE.md +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/GEMINI.md +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/README.md +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/analysis_results.md +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/main.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/__init__.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/api_keys.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/config.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/embeddings.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/i18n.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/planner.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/session.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/skills.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/structured.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/terminal.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/opalacoder/vcs.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/rename_project.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/scratch_tail.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/skills/generaldeveloper.md +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/skills/html_css_js.md +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/skills/opalacoder.md +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/skills/python_subprocess.md +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/skills/react_vite.md +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/tests/test_agent_config.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/tests/test_complexity_evaluator.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/tests/test_context_guard.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/tests/test_double_inference.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/tests/test_i18n_coverage.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/tests/test_orchestrator_prompt.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/tests/test_project_store.py +0 -0
- {opalacoder-0.1.0 → opalacoder-0.1.1}/tests/test_skills_loading.py +0 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# --- Current project settings ---
|
|
2
|
+
*.env
|
|
3
|
+
.env
|
|
4
|
+
*.env*
|
|
5
|
+
.venv
|
|
6
|
+
*.venv
|
|
7
|
+
node_modules
|
|
8
|
+
plan.md
|
|
9
|
+
|
|
10
|
+
# --- Typical Python Project settings ---
|
|
11
|
+
# Byte-compiled / optimized / DLL files
|
|
12
|
+
__pycache__/
|
|
13
|
+
*.py[cod]
|
|
14
|
+
*$py.class
|
|
15
|
+
|
|
16
|
+
# C extensions
|
|
17
|
+
*.so
|
|
18
|
+
|
|
19
|
+
# Distribution / packaging
|
|
20
|
+
.Python
|
|
21
|
+
build/
|
|
22
|
+
develop-eggs/
|
|
23
|
+
dist/
|
|
24
|
+
downloads/
|
|
25
|
+
eggs/
|
|
26
|
+
.eggs/
|
|
27
|
+
lib/
|
|
28
|
+
lib64/
|
|
29
|
+
parts/
|
|
30
|
+
sdist/
|
|
31
|
+
var/
|
|
32
|
+
wheels/
|
|
33
|
+
share/python-wheels/
|
|
34
|
+
*.egg-info/
|
|
35
|
+
.installed.cfg
|
|
36
|
+
*.egg
|
|
37
|
+
MANIFEST
|
|
38
|
+
|
|
39
|
+
# PyInstaller
|
|
40
|
+
# Usually these files are written by a python script from a template
|
|
41
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
42
|
+
*.manifest
|
|
43
|
+
*.spec
|
|
44
|
+
|
|
45
|
+
# Installer logs
|
|
46
|
+
pip-log.txt
|
|
47
|
+
pip-delete-this-directory.txt
|
|
48
|
+
|
|
49
|
+
# Unit test / coverage reports
|
|
50
|
+
htmlcov/
|
|
51
|
+
.tox/
|
|
52
|
+
.nox/
|
|
53
|
+
.coverage
|
|
54
|
+
.coverage.*
|
|
55
|
+
.cache
|
|
56
|
+
nosetests.xml
|
|
57
|
+
coverage.xml
|
|
58
|
+
*.cover
|
|
59
|
+
*.py,cover
|
|
60
|
+
.hypothesis/
|
|
61
|
+
.pytest_cache/
|
|
62
|
+
cover/
|
|
63
|
+
|
|
64
|
+
# Translations
|
|
65
|
+
*.mo
|
|
66
|
+
*.pot
|
|
67
|
+
|
|
68
|
+
# Django stuff:
|
|
69
|
+
*.log
|
|
70
|
+
local_settings.py
|
|
71
|
+
db.sqlite3
|
|
72
|
+
db.sqlite3-journal
|
|
73
|
+
|
|
74
|
+
# Flask stuff:
|
|
75
|
+
instance/
|
|
76
|
+
.webassets-cache
|
|
77
|
+
|
|
78
|
+
# Scrapy stuff:
|
|
79
|
+
.scrapy
|
|
80
|
+
|
|
81
|
+
# Sphinx documentation
|
|
82
|
+
docs/_build/
|
|
83
|
+
|
|
84
|
+
# PyBuilder
|
|
85
|
+
.pybuilder/
|
|
86
|
+
target/
|
|
87
|
+
|
|
88
|
+
# Jupyter Notebook
|
|
89
|
+
.ipynb_checkpoints
|
|
90
|
+
|
|
91
|
+
# IPython
|
|
92
|
+
profile_default/
|
|
93
|
+
ipython_config.py
|
|
94
|
+
|
|
95
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
96
|
+
__pypackages__/
|
|
97
|
+
|
|
98
|
+
# Celery stuff
|
|
99
|
+
celerybeat-schedule
|
|
100
|
+
celerybeat.pid
|
|
101
|
+
|
|
102
|
+
# SageMath parsed files
|
|
103
|
+
*.sage.py
|
|
104
|
+
|
|
105
|
+
# Environments
|
|
106
|
+
env/
|
|
107
|
+
venv/
|
|
108
|
+
ENV/
|
|
109
|
+
env.bak/
|
|
110
|
+
venv.bak/
|
|
111
|
+
|
|
112
|
+
# Spyder project settings
|
|
113
|
+
.spyderproject
|
|
114
|
+
.spyproject
|
|
115
|
+
|
|
116
|
+
# Rope project settings
|
|
117
|
+
.ropeproject
|
|
118
|
+
|
|
119
|
+
# mkdocs documentation
|
|
120
|
+
/site
|
|
121
|
+
|
|
122
|
+
# mypy
|
|
123
|
+
.mypy_cache/
|
|
124
|
+
.dmypy.json
|
|
125
|
+
dmypy.json
|
|
126
|
+
|
|
127
|
+
# Pyre type checker
|
|
128
|
+
.pyre/
|
|
129
|
+
|
|
130
|
+
# pytype static type analyzer
|
|
131
|
+
.pytype/
|
|
132
|
+
|
|
133
|
+
# Cython debug symbols
|
|
134
|
+
cython_debug/
|
|
135
|
+
|
|
136
|
+
# IDEs / Editors
|
|
137
|
+
.vscode/
|
|
138
|
+
.idea/
|
|
139
|
+
*.swp
|
|
140
|
+
*.swo
|
|
141
|
+
opalacoder/__pycache__/cli_commands.cpython-314.pyc
|
|
142
|
+
opalacoder/__pycache__/config.cpython-314.pyc
|
|
143
|
+
opalacoder/__pycache__/i18n.cpython-314.pyc
|
|
144
|
+
opalacoder/__pycache__/orchestrator.cpython-314.pyc
|
|
145
|
+
opalacoder/__pycache__/skills.cpython-314.pyc
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opalacoder
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Autonomous coding agent with interactive planning and modular execution
|
|
5
5
|
Project-URL: Homepage, https://github.com/gilzamir/OpalaCoder
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/gilzamir/OpalaCoder/issues
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import litellm
|
|
3
|
+
import os
|
|
4
|
+
from dotenv import load_dotenv
|
|
5
|
+
|
|
6
|
+
load_dotenv(os.path.expanduser("~/.opalacoder/.env"))
|
|
7
|
+
MODEL = "gemini/gemini-3-flash-preview"
|
|
8
|
+
|
|
9
|
+
async def test_tool_then_system():
|
|
10
|
+
tc = [{"id": "call_1", "type": "function", "function": {"name": "read_file", "arguments": "{}"}}]
|
|
11
|
+
tc_resp = {"role": "tool", "tool_call_id": "call_1", "name": "read_file", "content": "file contents"}
|
|
12
|
+
messages = [
|
|
13
|
+
{"role": "user", "content": "read my file"},
|
|
14
|
+
{"role": "assistant", "tool_calls": tc},
|
|
15
|
+
tc_resp,
|
|
16
|
+
{"role": "system", "content": "SYSTEM ALERT: Memory Pressure"}
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
res = await litellm.acompletion(model=MODEL, messages=messages)
|
|
21
|
+
with open("direct_test_result.txt", "w") as f:
|
|
22
|
+
f.write("SUCCESS")
|
|
23
|
+
except Exception as e:
|
|
24
|
+
with open("direct_test_result.txt", "w") as f:
|
|
25
|
+
f.write(f"ERROR: {str(e)}")
|
|
26
|
+
|
|
27
|
+
asyncio.run(test_tool_then_system())
|
|
Binary file
|
|
@@ -6,10 +6,11 @@ from agenticblocks.blocks.llm.memgpt_agent import MemGPTAgentBlock
|
|
|
6
6
|
from .config import DEFAULT_MODEL, LITELLM_DEFAULTS, get_agent_llm_kwargs, get_agent_model, get_agent_max_heartbeats, get_agent_debug
|
|
7
7
|
from . import i18n
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
def _make_llm(name: str, system_prompt: str, model: str | None, **kwargs) -> LLMAgentBlock:
|
|
9
|
+
def _make_llm(name: str, system_prompt: str, model: str | None, disable_lang_rule: bool = False, **kwargs) -> LLMAgentBlock:
|
|
11
10
|
lang_name = "English" if i18n._LANG == "en" else "Portuguese"
|
|
12
|
-
lang_rule =
|
|
11
|
+
lang_rule = ""
|
|
12
|
+
if not disable_lang_rule:
|
|
13
|
+
lang_rule = f"\n\nCRITICAL RULE: The user interface language is set to {lang_name}. You MUST translate your final responses, explanations, and output to {lang_name}. However, keep your internal reasoning, code variables, and logic in English."
|
|
13
14
|
|
|
14
15
|
resolved_model = get_agent_model(name, model or DEFAULT_MODEL)
|
|
15
16
|
|
|
@@ -91,6 +92,7 @@ Read each category carefully — they have clear, non-overlapping definitions.
|
|
|
91
92
|
Respond with ONLY ONE WORD from the list: command_hint, greetings, question, plan, chat.
|
|
92
93
|
No punctuation, no explanation.""",
|
|
93
94
|
model=model,
|
|
95
|
+
disable_lang_rule=True,
|
|
94
96
|
)
|
|
95
97
|
|
|
96
98
|
def make_complexity_evaluator(model: str | None = None) -> LLMAgentBlock:
|
|
@@ -105,6 +107,7 @@ Classify the user's request complexity into EXACTLY ONE of the following categor
|
|
|
105
107
|
|
|
106
108
|
Respond with ONLY ONE WORD from the list above. No punctuation, no explanation.""",
|
|
107
109
|
model=model,
|
|
110
|
+
disable_lang_rule=True,
|
|
108
111
|
)
|
|
109
112
|
|
|
110
113
|
|
|
@@ -123,7 +126,8 @@ Output ONLY valid JSON. No markdown formatting, no comments, no extra text.
|
|
|
123
126
|
Example: {"model": "default", "estimated_steps": 12}
|
|
124
127
|
""",
|
|
125
128
|
model=model,
|
|
126
|
-
|
|
129
|
+
disable_lang_rule=True,
|
|
130
|
+
litellm_kwargs={"response_format": {"type": "json_object"}}
|
|
127
131
|
)
|
|
128
132
|
|
|
129
133
|
|
|
@@ -183,6 +187,7 @@ Strict rules:
|
|
|
183
187
|
Do not explain, do not add anything else. Just: yes or no.
|
|
184
188
|
""",
|
|
185
189
|
model=model,
|
|
190
|
+
disable_lang_rule=True,
|
|
186
191
|
)
|
|
187
192
|
|
|
188
193
|
|
|
@@ -204,6 +209,7 @@ Rules:
|
|
|
204
209
|
Output: the refined plan only. No preamble, no explanation.
|
|
205
210
|
""",
|
|
206
211
|
model=model,
|
|
212
|
+
disable_lang_rule=True,
|
|
207
213
|
)
|
|
208
214
|
|
|
209
215
|
|
|
@@ -229,5 +235,5 @@ CRITICAL RULES:
|
|
|
229
235
|
3. ONLY select skills whose description perfectly matches the requested technologies.
|
|
230
236
|
""",
|
|
231
237
|
model=model,
|
|
238
|
+
disable_lang_rule=True,
|
|
232
239
|
)
|
|
233
|
-
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
# Singleton para evitar carregar o ChromaDB múltiplas vezes
|
|
5
|
+
_chroma_client = None
|
|
6
|
+
|
|
7
|
+
def _get_chroma_client():
|
|
8
|
+
global _chroma_client
|
|
9
|
+
if _chroma_client is None:
|
|
10
|
+
try:
|
|
11
|
+
import chromadb
|
|
12
|
+
except ImportError:
|
|
13
|
+
raise ImportError("Please install chromadb: pip install chromadb")
|
|
14
|
+
|
|
15
|
+
# O banco será salvo no mesmo diretório global do projects.db
|
|
16
|
+
# ex: ~/.opalacoder/chroma
|
|
17
|
+
db_path = Path.home() / ".opalacoder" / "chroma"
|
|
18
|
+
db_path.mkdir(parents=True, exist_ok=True)
|
|
19
|
+
|
|
20
|
+
_chroma_client = chromadb.PersistentClient(path=str(db_path))
|
|
21
|
+
return _chroma_client
|
|
22
|
+
|
|
23
|
+
def get_collection(project_name: str):
|
|
24
|
+
client = _get_chroma_client()
|
|
25
|
+
# Nomes de coleções no Chroma precisam ser alfanuméricos curtos
|
|
26
|
+
safe_name = "".join(c if c.isalnum() else "_" for c in project_name).strip("_")
|
|
27
|
+
if not safe_name:
|
|
28
|
+
safe_name = "default_project"
|
|
29
|
+
return client.get_or_create_collection(name=safe_name)
|
|
30
|
+
|
|
31
|
+
def append_to_archival(project_name: str, message_id: str, role: str, content: str, timestamp: str):
|
|
32
|
+
"""
|
|
33
|
+
Adiciona uma mensagem ao Archival Memory (ChromaDB) do projeto.
|
|
34
|
+
"""
|
|
35
|
+
if not content or not content.strip():
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
collection = get_collection(project_name)
|
|
39
|
+
|
|
40
|
+
metadata = {
|
|
41
|
+
"role": role,
|
|
42
|
+
"timestamp": timestamp
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
collection.add(
|
|
46
|
+
documents=[content],
|
|
47
|
+
metadatas=[metadata],
|
|
48
|
+
ids=[f"{message_id}"]
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def search_archival(project_name: str, query: str, limit: int = 5) -> list[dict]:
|
|
52
|
+
"""
|
|
53
|
+
Pesquisa o histórico usando similaridade de cosseno via ChromaDB.
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
collection = get_collection(project_name)
|
|
57
|
+
results = collection.query(
|
|
58
|
+
query_texts=[query],
|
|
59
|
+
n_results=limit
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
out = []
|
|
63
|
+
if results and "documents" in results and results["documents"]:
|
|
64
|
+
docs = results["documents"][0]
|
|
65
|
+
metas = results["metadatas"][0] if "metadatas" in results else []
|
|
66
|
+
for i, doc in enumerate(docs):
|
|
67
|
+
meta = metas[i] if i < len(metas) else {}
|
|
68
|
+
out.append({
|
|
69
|
+
"content": doc,
|
|
70
|
+
"role": meta.get("role", "unknown"),
|
|
71
|
+
"timestamp": meta.get("timestamp", "")
|
|
72
|
+
})
|
|
73
|
+
return out
|
|
74
|
+
except Exception as e:
|
|
75
|
+
import traceback
|
|
76
|
+
traceback.print_exc()
|
|
77
|
+
return []
|
|
@@ -97,10 +97,10 @@ async def _create_project(store: ProjectStore, args) -> ProjectData:
|
|
|
97
97
|
# ─── REPL Loop ────────────────────────────────────────────────────────────────
|
|
98
98
|
|
|
99
99
|
async def repl_loop(project: ProjectData, store: ProjectStore, max_retries: int) -> None:
|
|
100
|
-
from .tools import
|
|
100
|
+
from .tools import set_project_context
|
|
101
101
|
from .skills import load_project_skills
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
set_project_context(project, store)
|
|
104
104
|
project_skills = load_project_skills(project.project_path, project.skills)
|
|
105
105
|
|
|
106
106
|
T.section(f"Active Project: {_escape(project.project_name or project.name)}")
|
|
@@ -118,6 +118,18 @@ async def repl_loop(project: ProjectData, store: ProjectStore, max_retries: int)
|
|
|
118
118
|
else:
|
|
119
119
|
state.project.clear_state()
|
|
120
120
|
store.save(state.project)
|
|
121
|
+
else:
|
|
122
|
+
checkpoint_path = os.path.join(project.project_path, ".opalacoder", "session_state.json")
|
|
123
|
+
if os.path.exists(checkpoint_path):
|
|
124
|
+
T.warning("[yellow]Foi detectada uma execução de agente não finalizada (checkpoint salvo).[/yellow]")
|
|
125
|
+
choice = T.choose(_("resume_or_clear"), [_("resume"), _("clear")])
|
|
126
|
+
if choice == _("resume"):
|
|
127
|
+
await run_pipeline(state.project, store, max_retries, request="[RESUME_EXECUTION]", project_skills=state.project_skills)
|
|
128
|
+
else:
|
|
129
|
+
try:
|
|
130
|
+
os.remove(checkpoint_path)
|
|
131
|
+
except Exception:
|
|
132
|
+
pass
|
|
121
133
|
|
|
122
134
|
while True:
|
|
123
135
|
try:
|
|
@@ -142,7 +154,7 @@ async def repl_loop(project: ProjectData, store: ProjectStore, max_retries: int)
|
|
|
142
154
|
with T.spinner(_("agent_thinking")):
|
|
143
155
|
intent_res = await classifier.run(AgentInput(prompt=user_input))
|
|
144
156
|
_raw = intent_res.response.strip().lower()
|
|
145
|
-
intent = _raw.split()[0].
|
|
157
|
+
intent = _raw.split()[0].strip(".,!?*\"'") if _raw else ""
|
|
146
158
|
|
|
147
159
|
|
|
148
160
|
if not intent or intent not in _VALID_INTENTS:
|
|
@@ -110,7 +110,7 @@ async def cmd_list(state: REPLState, _args: list[str]) -> None:
|
|
|
110
110
|
|
|
111
111
|
@_registry.register("/load", usage="<name>", description="Load another project")
|
|
112
112
|
async def cmd_load(state: REPLState, args: list[str]) -> str | None:
|
|
113
|
-
from .tools import
|
|
113
|
+
from .tools import set_project_context
|
|
114
114
|
from .skills import load_project_skills
|
|
115
115
|
if not args:
|
|
116
116
|
T.error("Usage: /load <name>")
|
|
@@ -122,7 +122,7 @@ async def cmd_load(state: REPLState, args: list[str]) -> str | None:
|
|
|
122
122
|
loaded = state.store.load(name)
|
|
123
123
|
if loaded:
|
|
124
124
|
state.project = loaded
|
|
125
|
-
|
|
125
|
+
set_project_context(state.project, state.store)
|
|
126
126
|
state.project_skills = load_project_skills(state.project.project_path, state.project.skills)
|
|
127
127
|
state.chat_agent = make_chat_memgpt_agent(state.project.model)
|
|
128
128
|
T.success(f"Project '{name}' loaded.")
|