npcsh 1.0.29__tar.gz → 1.0.30__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.
- {npcsh-1.0.29 → npcsh-1.0.30}/PKG-INFO +1 -1
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/_state.py +1 -1
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/corca.py +20 -7
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/guac.py +31 -2
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npcsh.py +15 -22
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh.egg-info/PKG-INFO +1 -1
- {npcsh-1.0.29 → npcsh-1.0.30}/setup.py +1 -1
- {npcsh-1.0.29 → npcsh-1.0.30}/LICENSE +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/README.md +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/__init__.py +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/alicanto.py +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/mcp_helpers.py +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/mcp_server.py +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc.py +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/alicanto.npc +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/corca.npc +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/foreman.npc +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/frederic.npc +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/jinxs/bash_executer.jinx +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/jinxs/edit_file.jinx +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/jinxs/image_generation.jinx +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/jinxs/internet_search.jinx +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/jinxs/python_executor.jinx +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/jinxs/screen_cap.jinx +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/kadiefa.npc +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/npcsh.ctx +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/plonk.npc +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/plonkjr.npc +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/sibiji.npc +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/plonk.py +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/pti.py +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/routes.py +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/spool.py +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/wander.py +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh/yap.py +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh.egg-info/SOURCES.txt +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh.egg-info/dependency_links.txt +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh.egg-info/entry_points.txt +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh.egg-info/requires.txt +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/npcsh.egg-info/top_level.txt +0 -0
- {npcsh-1.0.29 → npcsh-1.0.30}/setup.cfg +0 -0
|
@@ -236,15 +236,20 @@ def process_mcp_stream(stream_response, active_npc):
|
|
|
236
236
|
tool_calls[idx]['function']['arguments'] += tool_call_delta.function.arguments
|
|
237
237
|
except KeyboardInterrupt:
|
|
238
238
|
interrupted = True
|
|
239
|
-
print('⚠️ Stream interrupted by user')
|
|
239
|
+
print('\n⚠️ Stream interrupted by user')
|
|
240
240
|
|
|
241
241
|
sys.stdout.write('\033[u')
|
|
242
|
-
sys.stdout.write('\033[
|
|
242
|
+
sys.stdout.write('\033[0J')
|
|
243
243
|
sys.stdout.flush()
|
|
244
244
|
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
if collected_content:
|
|
246
|
+
render_markdown(collected_content)
|
|
247
|
+
|
|
247
248
|
return collected_content, tool_calls
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
248
253
|
def execute_command_corca(command: str, state: ShellState, command_history, selected_mcp_tools_names: Optional[List[str]] = None) -> Tuple[ShellState, Any]:
|
|
249
254
|
mcp_tools_for_llm = []
|
|
250
255
|
|
|
@@ -265,6 +270,17 @@ def execute_command_corca(command: str, state: ShellState, command_history, sele
|
|
|
265
270
|
|
|
266
271
|
active_npc = state.npc if isinstance(state.npc, NPC) else NPC(name="default")
|
|
267
272
|
|
|
273
|
+
if len(state.messages) > 20:
|
|
274
|
+
compressed_state = active_npc.compress_planning_state({
|
|
275
|
+
"goal": "ongoing session",
|
|
276
|
+
"facts": [],
|
|
277
|
+
"successes": [],
|
|
278
|
+
"mistakes": [],
|
|
279
|
+
"todos": [],
|
|
280
|
+
"constraints": []
|
|
281
|
+
})
|
|
282
|
+
state.messages = [{"role": "system", "content": f"Session context: {compressed_state}"}]
|
|
283
|
+
|
|
268
284
|
response_dict = get_llm_response(
|
|
269
285
|
prompt=command,
|
|
270
286
|
npc=state.npc,
|
|
@@ -280,7 +296,6 @@ def execute_command_corca(command: str, state: ShellState, command_history, sele
|
|
|
280
296
|
|
|
281
297
|
collected_content, tool_calls = process_mcp_stream(stream_response, active_npc)
|
|
282
298
|
|
|
283
|
-
|
|
284
299
|
state.messages = messages
|
|
285
300
|
if collected_content or tool_calls:
|
|
286
301
|
assistant_message = {"role": "assistant", "content": collected_content}
|
|
@@ -293,8 +308,6 @@ def execute_command_corca(command: str, state: ShellState, command_history, sele
|
|
|
293
308
|
"tool_calls": tool_calls,
|
|
294
309
|
"messages": state.messages
|
|
295
310
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
311
|
def _resolve_and_copy_mcp_server_path(
|
|
299
312
|
explicit_path: Optional[str],
|
|
300
313
|
current_path: Optional[str],
|
|
@@ -1145,12 +1145,10 @@ def _get_guac_agent_emoji(failures: int, max_fail: int = 3) -> str:
|
|
|
1145
1145
|
|
|
1146
1146
|
|
|
1147
1147
|
|
|
1148
|
-
|
|
1149
1148
|
def _run_agentic_mode(command: str,
|
|
1150
1149
|
state: ShellState,
|
|
1151
1150
|
locals_dict: Dict[str, Any],
|
|
1152
1151
|
npc_team_dir: Path) -> Tuple[ShellState, Any]:
|
|
1153
|
-
"""Run agentic mode with continuous iteration based on progress"""
|
|
1154
1152
|
max_iterations = 5
|
|
1155
1153
|
iteration = 0
|
|
1156
1154
|
full_output = []
|
|
@@ -1158,6 +1156,37 @@ def _run_agentic_mode(command: str,
|
|
|
1158
1156
|
consecutive_failures = 0
|
|
1159
1157
|
max_consecutive_failures = 3
|
|
1160
1158
|
|
|
1159
|
+
if len(state.messages) > 15:
|
|
1160
|
+
planning_state = {
|
|
1161
|
+
"goal": "ongoing guac session",
|
|
1162
|
+
"facts": [f"Working in {state.current_path}", f"Variables: {list(locals_dict.keys())[:10]}"],
|
|
1163
|
+
"successes": [],
|
|
1164
|
+
"mistakes": [],
|
|
1165
|
+
"todos": [],
|
|
1166
|
+
"constraints": ["Focus on Python code execution", "Use existing variables when possible"]
|
|
1167
|
+
}
|
|
1168
|
+
compressed_state = state.npc.compress_planning_state(planning_state)
|
|
1169
|
+
state.messages = [{"role": "system", "content": f"Session context: {compressed_state}"}]
|
|
1170
|
+
|
|
1171
|
+
existing_vars_context = "EXISTING VARIABLES IN ENVIRONMENT:\n"
|
|
1172
|
+
for var_name, var_value in locals_dict.items():
|
|
1173
|
+
if not var_name.startswith('_') and var_name not in ['In', 'Out', 'exit', 'quit', 'get_ipython']:
|
|
1174
|
+
try:
|
|
1175
|
+
var_type = type(var_value).__name__
|
|
1176
|
+
var_repr = repr(var_value)
|
|
1177
|
+
if len(var_repr) > 100:
|
|
1178
|
+
var_repr = var_repr[:97] + "..."
|
|
1179
|
+
existing_vars_context += f"- {var_name} ({var_type}): {var_repr}\n"
|
|
1180
|
+
except:
|
|
1181
|
+
existing_vars_context += f"- {var_name} ({type(var_value).__name__}): <unrepresentable>\n"
|
|
1182
|
+
previous_code = ''
|
|
1183
|
+
next_step = ''
|
|
1184
|
+
steps = []
|
|
1185
|
+
while iteration < max_iterations and consecutive_failures < max_consecutive_failures:
|
|
1186
|
+
iteration += 1
|
|
1187
|
+
print(f"\n{_get_guac_agent_emoji(consecutive_failures, max_consecutive_failures)} Agentic iteration {iteration} ")
|
|
1188
|
+
|
|
1189
|
+
|
|
1161
1190
|
|
|
1162
1191
|
existing_vars_context = "EXISTING VARIABLES IN ENVIRONMENT:\n"
|
|
1163
1192
|
for var_name, var_value in locals_dict.items():
|
|
@@ -68,22 +68,14 @@ Begin by asking a question, issuing a bash command, or typing '/help' for more i
|
|
|
68
68
|
)
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
|
|
72
71
|
def run_repl(command_history: CommandHistory, initial_state: ShellState):
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
'''
|
|
76
|
-
Func for running the npcsh repl
|
|
77
|
-
'''
|
|
78
72
|
state = initial_state
|
|
79
73
|
print_welcome_message()
|
|
80
74
|
|
|
81
|
-
|
|
82
75
|
render_markdown(f'- Using {state.current_mode} mode. Use /agent, /cmd, or /chat to switch to other modes')
|
|
83
76
|
render_markdown(f'- To switch to a different NPC, type /npc <npc_name> or /n <npc_name> to switch to that NPC.')
|
|
84
77
|
render_markdown('\n- Here are the current NPCs available in your team: ' + ', '.join([npc_name for npc_name in state.team.npcs.keys()]))
|
|
85
78
|
|
|
86
|
-
|
|
87
79
|
is_windows = platform.system().lower().startswith("win")
|
|
88
80
|
try:
|
|
89
81
|
completer = make_completer(state)
|
|
@@ -92,23 +84,16 @@ def run_repl(command_history: CommandHistory, initial_state: ShellState):
|
|
|
92
84
|
pass
|
|
93
85
|
session_scopes = set()
|
|
94
86
|
|
|
95
|
-
|
|
96
87
|
def exit_shell(current_state: ShellState):
|
|
97
|
-
"""
|
|
98
|
-
On exit, iterates through all active scopes from the session and
|
|
99
|
-
creates/updates the specific knowledge graph for each one.
|
|
100
|
-
"""
|
|
101
88
|
print("\nGoodbye!")
|
|
102
89
|
print(colored("Processing and archiving all session knowledge...", "cyan"))
|
|
103
90
|
|
|
104
91
|
engine = command_history.engine
|
|
105
92
|
|
|
106
|
-
|
|
107
93
|
for team_name, npc_name, path in session_scopes:
|
|
108
94
|
try:
|
|
109
95
|
print(f" -> Archiving knowledge for: T='{team_name}', N='{npc_name}', P='{path}'")
|
|
110
96
|
|
|
111
|
-
|
|
112
97
|
convo_id = current_state.conversation_id
|
|
113
98
|
all_messages = command_history.get_conversations_by_id(convo_id)
|
|
114
99
|
|
|
@@ -123,10 +108,8 @@ def run_repl(command_history: CommandHistory, initial_state: ShellState):
|
|
|
123
108
|
print(" ...No content for this scope, skipping.")
|
|
124
109
|
continue
|
|
125
110
|
|
|
126
|
-
|
|
127
111
|
current_kg = load_kg_from_db(engine, team_name, npc_name, path)
|
|
128
112
|
|
|
129
|
-
|
|
130
113
|
evolved_kg, _ = kg_evolve_incremental(
|
|
131
114
|
existing_kg=current_kg,
|
|
132
115
|
new_content_text=full_text,
|
|
@@ -137,10 +120,8 @@ def run_repl(command_history: CommandHistory, initial_state: ShellState):
|
|
|
137
120
|
link_concepts_facts = True,
|
|
138
121
|
link_concepts_concepts = True,
|
|
139
122
|
link_facts_facts = True,
|
|
140
|
-
|
|
141
123
|
)
|
|
142
124
|
|
|
143
|
-
|
|
144
125
|
save_kg_to_db(engine,
|
|
145
126
|
evolved_kg,
|
|
146
127
|
team_name,
|
|
@@ -154,10 +135,20 @@ def run_repl(command_history: CommandHistory, initial_state: ShellState):
|
|
|
154
135
|
|
|
155
136
|
sys.exit(0)
|
|
156
137
|
|
|
157
|
-
|
|
158
|
-
|
|
159
138
|
while True:
|
|
160
139
|
try:
|
|
140
|
+
if len(state.messages) > 20:
|
|
141
|
+
planning_state = {
|
|
142
|
+
"goal": "ongoing npcsh session",
|
|
143
|
+
"facts": [f"Working in {state.current_path}", f"Current mode: {state.current_mode}"],
|
|
144
|
+
"successes": [],
|
|
145
|
+
"mistakes": [],
|
|
146
|
+
"todos": [],
|
|
147
|
+
"constraints": ["Follow user requests", "Use appropriate mode for tasks"]
|
|
148
|
+
}
|
|
149
|
+
compressed_state = state.npc.compress_planning_state(planning_state)
|
|
150
|
+
state.messages = [{"role": "system", "content": f"Session context: {compressed_state}"}]
|
|
151
|
+
|
|
161
152
|
try:
|
|
162
153
|
completer = make_completer(state)
|
|
163
154
|
readline.set_completer(completer)
|
|
@@ -198,6 +189,7 @@ def run_repl(command_history: CommandHistory, initial_state: ShellState):
|
|
|
198
189
|
continue
|
|
199
190
|
else:
|
|
200
191
|
exit_shell(state)
|
|
192
|
+
|
|
201
193
|
team_name = state.team.name if state.team else "__none__"
|
|
202
194
|
npc_name = state.npc.name if isinstance(state.npc, NPC) else "__none__"
|
|
203
195
|
session_scopes.add((team_name, npc_name, state.current_path))
|
|
@@ -224,7 +216,8 @@ def run_repl(command_history: CommandHistory, initial_state: ShellState):
|
|
|
224
216
|
if is_windows and "EOF" in str(e).lower():
|
|
225
217
|
print("\nHint: On Windows, use Ctrl+Z then Enter for EOF, or type 'exit'")
|
|
226
218
|
continue
|
|
227
|
-
raise
|
|
219
|
+
raise
|
|
220
|
+
|
|
228
221
|
|
|
229
222
|
def main() -> None:
|
|
230
223
|
parser = argparse.ArgumentParser(description="npcsh - An NPC-powered shell.")
|
|
@@ -78,7 +78,7 @@ extra_files = package_files("npcsh/npc_team/")
|
|
|
78
78
|
|
|
79
79
|
setup(
|
|
80
80
|
name="npcsh",
|
|
81
|
-
version="1.0.
|
|
81
|
+
version="1.0.30",
|
|
82
82
|
packages=find_packages(exclude=["tests*"]),
|
|
83
83
|
install_requires=base_requirements, # Only install base requirements by default
|
|
84
84
|
extras_require={
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|