nimcode 0.4.2__tar.gz → 0.4.5__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.
- {nimcode-0.4.2 → nimcode-0.4.5}/PKG-INFO +1 -1
- nimcode-0.4.5/src/nimcode/__version__.py +1 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/agent.py +22 -4
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/memory.py +4 -2
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/repl.py +4 -2
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode.egg-info/PKG-INFO +1 -1
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_agent.py +2 -2
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_memory.py +1 -1
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_version.py +4 -3
- nimcode-0.4.2/src/nimcode/__version__.py +0 -1
- {nimcode-0.4.2 → nimcode-0.4.5}/README.md +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/setup.cfg +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/setup.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/__init__.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/cli.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/config.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/lenient_parser.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/mcp_client.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/model_registry.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/nim_client.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/permissions.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/plugin_manager.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/rag.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/repo_map.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/tools.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/updater.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode/watcher.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode.egg-info/SOURCES.txt +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode.egg-info/dependency_links.txt +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode.egg-info/entry_points.txt +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode.egg-info/requires.txt +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/src/nimcode.egg-info/top_level.txt +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_cli.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_config.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_lenient_parser.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_mcp_client.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_model_registry.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_nim_client.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_permissions.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_plan_injection.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_plugin_manager.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_repl.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_repl_extra.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_repl_fixes.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_repl_trust.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_repo_map.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_tools.py +0 -0
- {nimcode-0.4.2 → nimcode-0.4.5}/tests/test_updater.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.5"
|
|
@@ -150,6 +150,17 @@ class Agent:
|
|
|
150
150
|
final_prompt += "\n\nCRITICAL USER SKILLS & GUIDELINES:\n" + "\n".join(loaded_skills)
|
|
151
151
|
logger.info(f"Loaded {len(loaded_skills)} custom skills from {skills_dir}")
|
|
152
152
|
|
|
153
|
+
# Active Plan Context
|
|
154
|
+
active_plan_path = os.path.join(cwd, ".nimcode", "active_plan.txt")
|
|
155
|
+
if os.path.exists(active_plan_path):
|
|
156
|
+
try:
|
|
157
|
+
with open(active_plan_path, "r", encoding="utf-8") as f:
|
|
158
|
+
plan_file = f.read().strip()
|
|
159
|
+
if plan_file:
|
|
160
|
+
final_prompt += f"\n\nCURRENT ACTIVE PLAN:\nThe user has set '{plan_file}' as the active implementation plan. You MUST read this plan from the file system and follow its step-by-step instructions for your current coding tasks."
|
|
161
|
+
except Exception as e:
|
|
162
|
+
logger.warning(f"Failed to read active plan: {e}")
|
|
163
|
+
|
|
153
164
|
# Git context
|
|
154
165
|
if os.path.exists(".git"):
|
|
155
166
|
import subprocess
|
|
@@ -327,7 +338,7 @@ class Agent:
|
|
|
327
338
|
return "Subagent reached max turns before completing."
|
|
328
339
|
|
|
329
340
|
async def _distill_memory(self) -> list:
|
|
330
|
-
system_msg = self.messages[0]
|
|
341
|
+
system_msg = dict(self.messages[0])
|
|
331
342
|
recent_msgs = self.messages[-4:] if len(self.messages) > 4 else self.messages[1:]
|
|
332
343
|
old_msgs = self.messages[1:-4] if len(self.messages) > 4 else []
|
|
333
344
|
|
|
@@ -335,16 +346,23 @@ class Agent:
|
|
|
335
346
|
return self.messages
|
|
336
347
|
|
|
337
348
|
summary_prompt = "You are a summarization AI. Please summarize the following conversation concisely. Focus on the final state, any completed goals, and any context needed for the next steps:\n\n"
|
|
349
|
+
|
|
350
|
+
existing_summary_marker = "\n\n[PREVIOUS MEMORY SUMMARY]\n"
|
|
351
|
+
if existing_summary_marker in system_msg["content"]:
|
|
352
|
+
parts = system_msg["content"].split(existing_summary_marker, 1)
|
|
353
|
+
system_msg["content"] = parts[0]
|
|
354
|
+
summary_prompt += f"Previous summary to include:\n{parts[1]}\n\n"
|
|
355
|
+
|
|
338
356
|
for m in old_msgs:
|
|
339
357
|
summary_prompt += f"{m['role'].upper()}: {str(m['content'])[:500]}...\n"
|
|
340
358
|
|
|
341
359
|
try:
|
|
342
360
|
summary = await self.client.chat_one_shot(summary_prompt)
|
|
343
|
-
system_msg["content"] += f"
|
|
361
|
+
system_msg["content"] += f"{existing_summary_marker}{summary}"
|
|
362
|
+
return [system_msg] + recent_msgs
|
|
344
363
|
except Exception as e:
|
|
345
364
|
logger.error(f"Distillation failed: {e}")
|
|
346
|
-
|
|
347
|
-
return [system_msg] + recent_msgs
|
|
365
|
+
return self.messages
|
|
348
366
|
|
|
349
367
|
async def run(self, initial_prompt: str = None) -> None:
|
|
350
368
|
"""Main execution loop for NimCode agent."""
|
|
@@ -60,8 +60,10 @@ class MemoryManager:
|
|
|
60
60
|
|
|
61
61
|
@staticmethod
|
|
62
62
|
def log_to_nimcode_md(turn: int, prompt: str, response: str, cwd: str = ".") -> None:
|
|
63
|
-
"""Appends the interaction to NIMCODE.md for persistent session history."""
|
|
64
|
-
|
|
63
|
+
"""Appends the interaction to .nimcode/NIMCODE.md for persistent session history."""
|
|
64
|
+
nimcode_dir = os.path.join(cwd, ".nimcode")
|
|
65
|
+
os.makedirs(nimcode_dir, exist_ok=True)
|
|
66
|
+
file_path = os.path.join(nimcode_dir, "NIMCODE.md")
|
|
65
67
|
with open(file_path, "a", encoding="utf-8") as f:
|
|
66
68
|
f.write(f"## Turn {turn}\n\n")
|
|
67
69
|
f.write(f"**User**: {prompt}\n\n")
|
|
@@ -443,14 +443,16 @@ class NimcodeREPL:
|
|
|
443
443
|
"2. BASE your plan on the ACTUAL content of those documents/files. Quote real names, real modules, "
|
|
444
444
|
"real requirements found during exploration. Do NOT invent a generic project.\n"
|
|
445
445
|
"3. Write the plan to a file inside '.nimcode/plans/'. CHOOSE a descriptive name derived from the task "
|
|
446
|
-
"or document (e.g. '
|
|
446
|
+
"or document AND append a timestamp to prevent naming collisions (e.g. 'imagease_pro_plan_20260730_1430.md').\n"
|
|
447
447
|
"4. The plan must reference real files, real code, and real requirements found during exploration. "
|
|
448
448
|
"Include specific files to create/modify (with full paths), actual code snippets or pseudocode for each "
|
|
449
449
|
"change, dependencies to install, testing strategy, and step-by-step execution order.\n"
|
|
450
450
|
"5. NEVER output a generic software-lifecycle template (Phase1: Research / Design / Development / "
|
|
451
451
|
"Testing / Deployment) unless that is literally what the documents describe. NEVER use placeholders "
|
|
452
452
|
"like [Insert Date].\n"
|
|
453
|
-
"6. A good plan is one that another developer could follow to implement the feature without asking questions
|
|
453
|
+
"6. A good plan is one that another developer could follow to implement the feature without asking questions.\n"
|
|
454
|
+
"7. After writing the plan, you MUST overwrite the file '.nimcode/active_plan.txt' to contain exactly the path "
|
|
455
|
+
"of the plan you just created. This ensures the system tracks the current active plan."
|
|
454
456
|
)})
|
|
455
457
|
continue
|
|
456
458
|
elif user_input.strip().startswith("/teleport"):
|
|
@@ -99,8 +99,8 @@ async def test_distill_memory(agent):
|
|
|
99
99
|
agent.client.chat_one_shot = AsyncMock(return_value="Summarized history")
|
|
100
100
|
|
|
101
101
|
new_msgs = await agent._distill_memory()
|
|
102
|
-
assert "[PREVIOUS MEMORY SUMMARY]" in
|
|
103
|
-
assert "Summarized history" in
|
|
102
|
+
assert "[PREVIOUS MEMORY SUMMARY]" in new_msgs[0]["content"]
|
|
103
|
+
assert "Summarized history" in new_msgs[0]["content"]
|
|
104
104
|
assert len(new_msgs) == 5
|
|
105
105
|
|
|
106
106
|
@pytest.mark.asyncio
|
|
@@ -70,7 +70,7 @@ def test_compact_context_empty():
|
|
|
70
70
|
def test_log_to_nimcode_md(tmp_path):
|
|
71
71
|
cwd = str(tmp_path)
|
|
72
72
|
MemoryManager.log_to_nimcode_md(1, "hello", "world", cwd)
|
|
73
|
-
file_path = os.path.join(cwd, "NIMCODE.md")
|
|
73
|
+
file_path = os.path.join(cwd, ".nimcode", "NIMCODE.md")
|
|
74
74
|
assert os.path.exists(file_path)
|
|
75
75
|
with open(file_path, "r", encoding="utf-8") as f:
|
|
76
76
|
content = f.read()
|
|
@@ -11,10 +11,11 @@ def test_version_format():
|
|
|
11
11
|
assert part.isdigit(), f"Version part '{part}' is not a digit"
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def
|
|
15
|
-
"""Current version should be
|
|
14
|
+
def test_version_is_valid():
|
|
15
|
+
"""Current version should be a valid string."""
|
|
16
16
|
from nimcode.__version__ import __version__
|
|
17
|
-
assert __version__
|
|
17
|
+
assert isinstance(__version__, str)
|
|
18
|
+
assert len(__version__.split('.')) >= 3
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
def test_updater_uses_version():
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.4.2"
|
|
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
|