npcsh 0.3.32__py3-none-any.whl → 1.0.1__py3-none-any.whl
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/_state.py +942 -0
- npcsh/alicanto.py +1074 -0
- npcsh/guac.py +785 -0
- npcsh/mcp_helpers.py +357 -0
- npcsh/mcp_npcsh.py +822 -0
- npcsh/mcp_server.py +184 -0
- npcsh/npc.py +218 -0
- npcsh/npcsh.py +1161 -0
- npcsh/plonk.py +387 -269
- npcsh/pti.py +234 -0
- npcsh/routes.py +958 -0
- npcsh/spool.py +315 -0
- npcsh/wander.py +550 -0
- npcsh/yap.py +573 -0
- npcsh-1.0.1.dist-info/METADATA +596 -0
- npcsh-1.0.1.dist-info/RECORD +21 -0
- {npcsh-0.3.32.dist-info → npcsh-1.0.1.dist-info}/WHEEL +1 -1
- npcsh-1.0.1.dist-info/entry_points.txt +9 -0
- {npcsh-0.3.32.dist-info → npcsh-1.0.1.dist-info}/licenses/LICENSE +1 -1
- npcsh/audio.py +0 -569
- npcsh/audio_gen.py +0 -1
- npcsh/cli.py +0 -543
- npcsh/command_history.py +0 -566
- npcsh/conversation.py +0 -54
- npcsh/data_models.py +0 -46
- npcsh/dataframes.py +0 -171
- npcsh/embeddings.py +0 -168
- npcsh/helpers.py +0 -646
- npcsh/image.py +0 -298
- npcsh/image_gen.py +0 -79
- npcsh/knowledge_graph.py +0 -1006
- npcsh/llm_funcs.py +0 -2195
- npcsh/load_data.py +0 -83
- npcsh/main.py +0 -5
- npcsh/model_runner.py +0 -189
- npcsh/npc_compiler.py +0 -2879
- npcsh/npc_sysenv.py +0 -388
- npcsh/npc_team/assembly_lines/test_pipeline.py +0 -181
- npcsh/npc_team/corca.npc +0 -13
- npcsh/npc_team/foreman.npc +0 -7
- npcsh/npc_team/npcsh.ctx +0 -11
- npcsh/npc_team/sibiji.npc +0 -4
- npcsh/npc_team/templates/analytics/celona.npc +0 -0
- npcsh/npc_team/templates/hr_support/raone.npc +0 -0
- npcsh/npc_team/templates/humanities/eriane.npc +0 -4
- npcsh/npc_team/templates/it_support/lineru.npc +0 -0
- npcsh/npc_team/templates/marketing/slean.npc +0 -4
- npcsh/npc_team/templates/philosophy/maurawa.npc +0 -0
- npcsh/npc_team/templates/sales/turnic.npc +0 -4
- npcsh/npc_team/templates/software/welxor.npc +0 -0
- npcsh/npc_team/tools/bash_executer.tool +0 -32
- npcsh/npc_team/tools/calculator.tool +0 -8
- npcsh/npc_team/tools/code_executor.tool +0 -16
- npcsh/npc_team/tools/generic_search.tool +0 -27
- npcsh/npc_team/tools/image_generation.tool +0 -25
- npcsh/npc_team/tools/local_search.tool +0 -149
- npcsh/npc_team/tools/npcsh_executor.tool +0 -9
- npcsh/npc_team/tools/screen_cap.tool +0 -27
- npcsh/npc_team/tools/sql_executor.tool +0 -26
- npcsh/response.py +0 -272
- npcsh/search.py +0 -252
- npcsh/serve.py +0 -1467
- npcsh/shell.py +0 -524
- npcsh/shell_helpers.py +0 -3919
- npcsh/stream.py +0 -233
- npcsh/video.py +0 -52
- npcsh/video_gen.py +0 -69
- npcsh-0.3.32.data/data/npcsh/npc_team/bash_executer.tool +0 -32
- npcsh-0.3.32.data/data/npcsh/npc_team/calculator.tool +0 -8
- npcsh-0.3.32.data/data/npcsh/npc_team/celona.npc +0 -0
- npcsh-0.3.32.data/data/npcsh/npc_team/code_executor.tool +0 -16
- npcsh-0.3.32.data/data/npcsh/npc_team/corca.npc +0 -13
- npcsh-0.3.32.data/data/npcsh/npc_team/eriane.npc +0 -4
- npcsh-0.3.32.data/data/npcsh/npc_team/foreman.npc +0 -7
- npcsh-0.3.32.data/data/npcsh/npc_team/generic_search.tool +0 -27
- npcsh-0.3.32.data/data/npcsh/npc_team/image_generation.tool +0 -25
- npcsh-0.3.32.data/data/npcsh/npc_team/lineru.npc +0 -0
- npcsh-0.3.32.data/data/npcsh/npc_team/local_search.tool +0 -149
- npcsh-0.3.32.data/data/npcsh/npc_team/maurawa.npc +0 -0
- npcsh-0.3.32.data/data/npcsh/npc_team/npcsh.ctx +0 -11
- npcsh-0.3.32.data/data/npcsh/npc_team/npcsh_executor.tool +0 -9
- npcsh-0.3.32.data/data/npcsh/npc_team/raone.npc +0 -0
- npcsh-0.3.32.data/data/npcsh/npc_team/screen_cap.tool +0 -27
- npcsh-0.3.32.data/data/npcsh/npc_team/sibiji.npc +0 -4
- npcsh-0.3.32.data/data/npcsh/npc_team/slean.npc +0 -4
- npcsh-0.3.32.data/data/npcsh/npc_team/sql_executor.tool +0 -26
- npcsh-0.3.32.data/data/npcsh/npc_team/test_pipeline.py +0 -181
- npcsh-0.3.32.data/data/npcsh/npc_team/turnic.npc +0 -4
- npcsh-0.3.32.data/data/npcsh/npc_team/welxor.npc +0 -0
- npcsh-0.3.32.dist-info/METADATA +0 -779
- npcsh-0.3.32.dist-info/RECORD +0 -78
- npcsh-0.3.32.dist-info/entry_points.txt +0 -3
- {npcsh-0.3.32.dist-info → npcsh-1.0.1.dist-info}/top_level.txt +0 -0
npcsh/helpers.py
DELETED
|
@@ -1,646 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from typing import List, Dict, Any, Optional
|
|
3
|
-
import os
|
|
4
|
-
import sqlite3
|
|
5
|
-
import subprocess
|
|
6
|
-
import platform
|
|
7
|
-
import yaml
|
|
8
|
-
|
|
9
|
-
try:
|
|
10
|
-
import nltk
|
|
11
|
-
except:
|
|
12
|
-
print("Error importing nltk")
|
|
13
|
-
import numpy as np
|
|
14
|
-
|
|
15
|
-
import filecmp
|
|
16
|
-
|
|
17
|
-
import shutil
|
|
18
|
-
import tempfile
|
|
19
|
-
import pandas as pd
|
|
20
|
-
|
|
21
|
-
try:
|
|
22
|
-
from sentence_transformers import util
|
|
23
|
-
except Exception as e:
|
|
24
|
-
print(f"Error importing sentence_transformers: {e}")
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def get_shell_config_file() -> str:
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
Function Description:
|
|
31
|
-
This function returns the path to the shell configuration file.
|
|
32
|
-
Args:
|
|
33
|
-
None
|
|
34
|
-
Keyword Args:
|
|
35
|
-
None
|
|
36
|
-
Returns:
|
|
37
|
-
The path to the shell configuration file.
|
|
38
|
-
"""
|
|
39
|
-
# Check the current shell
|
|
40
|
-
shell = os.environ.get("SHELL", "")
|
|
41
|
-
|
|
42
|
-
if "zsh" in shell:
|
|
43
|
-
return os.path.expanduser("~/.zshrc")
|
|
44
|
-
elif "bash" in shell:
|
|
45
|
-
# On macOS, use .bash_profile for login shells
|
|
46
|
-
if platform.system() == "Darwin":
|
|
47
|
-
return os.path.expanduser("~/.bash_profile")
|
|
48
|
-
else:
|
|
49
|
-
return os.path.expanduser("~/.bashrc")
|
|
50
|
-
else:
|
|
51
|
-
# Default to .bashrc if we can't determine the shell
|
|
52
|
-
return os.path.expanduser("~/.bashrc")
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def initial_table_print(cursor: sqlite3.Cursor) -> None:
|
|
56
|
-
"""
|
|
57
|
-
Function Description:
|
|
58
|
-
This function is used to print the initial table.
|
|
59
|
-
Args:
|
|
60
|
-
cursor : sqlite3.Cursor : The SQLite cursor.
|
|
61
|
-
Keyword Args:
|
|
62
|
-
None
|
|
63
|
-
Returns:
|
|
64
|
-
None
|
|
65
|
-
"""
|
|
66
|
-
|
|
67
|
-
cursor.execute(
|
|
68
|
-
"SELECT name FROM sqlite_master WHERE type='table' AND name != 'command_history'"
|
|
69
|
-
)
|
|
70
|
-
tables = cursor.fetchall()
|
|
71
|
-
|
|
72
|
-
print("\nAvailable tables:")
|
|
73
|
-
for i, table in enumerate(tables, 1):
|
|
74
|
-
print(f"{i}. {table[0]}")
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def ensure_npcshrc_exists() -> str:
|
|
78
|
-
"""
|
|
79
|
-
Function Description:
|
|
80
|
-
This function ensures that the .npcshrc file exists in the user's home directory.
|
|
81
|
-
Args:
|
|
82
|
-
None
|
|
83
|
-
Keyword Args:
|
|
84
|
-
None
|
|
85
|
-
Returns:
|
|
86
|
-
The path to the .npcshrc file.
|
|
87
|
-
"""
|
|
88
|
-
|
|
89
|
-
npcshrc_path = os.path.expanduser("~/.npcshrc")
|
|
90
|
-
if not os.path.exists(npcshrc_path):
|
|
91
|
-
with open(npcshrc_path, "w") as npcshrc:
|
|
92
|
-
npcshrc.write("# NPCSH Configuration File\n")
|
|
93
|
-
npcshrc.write("export NPCSH_INITIALIZED=0\n")
|
|
94
|
-
npcshrc.write("export NPCSH_DEFAULT_MODE='chat'\n")
|
|
95
|
-
npcshrc.write("export NPCSH_CHAT_PROVIDER='ollama'\n")
|
|
96
|
-
npcshrc.write("export NPCSH_CHAT_MODEL='llama3.2'\n")
|
|
97
|
-
npcshrc.write("export NPCSH_REASONING_PROVIDER='ollama'\n")
|
|
98
|
-
npcshrc.write("export NPCSH_REASONING_MODEL='deepseek-r1'\n")
|
|
99
|
-
|
|
100
|
-
npcshrc.write("export NPCSH_EMBEDDING_PROVIDER='ollama'\n")
|
|
101
|
-
npcshrc.write("export NPCSH_EMBEDDING_MODEL='nomic-embed-text'\n")
|
|
102
|
-
npcshrc.write("export NPCSH_VISION_PROVIDER='ollama'\n")
|
|
103
|
-
npcshrc.write("export NPCSH_VISION_MODEL='llava7b'\n")
|
|
104
|
-
npcshrc.write(
|
|
105
|
-
"export NPCSH_IMAGE_GEN_MODEL='runwayml/stable-diffusion-v1-5'\n"
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
npcshrc.write("export NPCSH_IMAGE_GEN_PROVIDER='diffusers'\n")
|
|
109
|
-
npcshrc.write(
|
|
110
|
-
"export NPCSH_VIDEO_GEN_MODEL='runwayml/stable-diffusion-v1-5'\n"
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
npcshrc.write("export NPCSH_VIDEO_GEN_PROVIDER='diffusers'\n")
|
|
114
|
-
|
|
115
|
-
npcshrc.write("export NPCSH_API_URL=''\n")
|
|
116
|
-
npcshrc.write("export NPCSH_DB_PATH='~/npcsh_history.db'\n")
|
|
117
|
-
npcshrc.write("export NPCSH_VECTOR_DB_PATH='~/npcsh_chroma.db'\n")
|
|
118
|
-
npcshrc.write("export NPCSH_STREAM_OUTPUT=0")
|
|
119
|
-
return npcshrc_path
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
# Function to check and download NLTK data if necessary
|
|
123
|
-
def ensure_nltk_punkt() -> None:
|
|
124
|
-
"""
|
|
125
|
-
Function Description:
|
|
126
|
-
This function ensures that the NLTK 'punkt' tokenizer is downloaded.
|
|
127
|
-
Args:
|
|
128
|
-
None
|
|
129
|
-
Keyword Args:
|
|
130
|
-
None
|
|
131
|
-
Returns:
|
|
132
|
-
None
|
|
133
|
-
"""
|
|
134
|
-
|
|
135
|
-
try:
|
|
136
|
-
nltk.data.find("tokenizers/punkt")
|
|
137
|
-
except LookupError:
|
|
138
|
-
print("Downloading NLTK 'punkt' tokenizer...")
|
|
139
|
-
nltk.download("punkt")
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def load_all_files(
|
|
143
|
-
directory: str, extensions: List[str] = None, depth: int = 1
|
|
144
|
-
) -> Dict[str, str]:
|
|
145
|
-
"""
|
|
146
|
-
Function Description:
|
|
147
|
-
This function loads all text files in a directory and its subdirectories.
|
|
148
|
-
Args:
|
|
149
|
-
directory: The directory to search.
|
|
150
|
-
Keyword Args:
|
|
151
|
-
extensions: A list of file extensions to include.
|
|
152
|
-
depth: The depth of subdirectories to search.
|
|
153
|
-
Returns:
|
|
154
|
-
A dictionary with file paths as keys and file contents as values.
|
|
155
|
-
"""
|
|
156
|
-
text_data = {}
|
|
157
|
-
if depth < 1:
|
|
158
|
-
return text_data # Reached the specified depth, stop recursion.
|
|
159
|
-
|
|
160
|
-
if extensions is None:
|
|
161
|
-
# Default to common text file extensions
|
|
162
|
-
extensions = [
|
|
163
|
-
".txt",
|
|
164
|
-
".md",
|
|
165
|
-
".py",
|
|
166
|
-
".java",
|
|
167
|
-
".c",
|
|
168
|
-
".cpp",
|
|
169
|
-
".html",
|
|
170
|
-
".css",
|
|
171
|
-
".js",
|
|
172
|
-
".ts",
|
|
173
|
-
".tsx",
|
|
174
|
-
".npc",
|
|
175
|
-
# Add more extensions if needed
|
|
176
|
-
]
|
|
177
|
-
|
|
178
|
-
try:
|
|
179
|
-
# List all entries in the directory
|
|
180
|
-
entries = os.listdir(directory)
|
|
181
|
-
except Exception as e:
|
|
182
|
-
print(f"Could not list directory {directory}: {e}")
|
|
183
|
-
return text_data
|
|
184
|
-
|
|
185
|
-
for entry in entries:
|
|
186
|
-
path = os.path.join(directory, entry)
|
|
187
|
-
if os.path.isfile(path):
|
|
188
|
-
if any(path.endswith(ext) for ext in extensions):
|
|
189
|
-
try:
|
|
190
|
-
with open(path, "r", encoding="utf-8", errors="ignore") as file:
|
|
191
|
-
text_data[path] = file.read()
|
|
192
|
-
except Exception as e:
|
|
193
|
-
print(f"Could not read file {path}: {e}")
|
|
194
|
-
elif os.path.isdir(path):
|
|
195
|
-
# Recurse into subdirectories, decreasing depth by 1
|
|
196
|
-
subdir_data = load_all_files(path, extensions, depth=depth - 1)
|
|
197
|
-
text_data.update(subdir_data)
|
|
198
|
-
|
|
199
|
-
return text_data
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
def add_npcshrc_to_shell_config() -> None:
|
|
203
|
-
"""
|
|
204
|
-
Function Description:
|
|
205
|
-
This function adds the sourcing of the .npcshrc file to the user's shell configuration file.
|
|
206
|
-
Args:
|
|
207
|
-
None
|
|
208
|
-
Keyword Args:
|
|
209
|
-
None
|
|
210
|
-
Returns:
|
|
211
|
-
None
|
|
212
|
-
"""
|
|
213
|
-
|
|
214
|
-
if os.getenv("NPCSH_INITIALIZED") is not None:
|
|
215
|
-
return
|
|
216
|
-
config_file = get_shell_config_file()
|
|
217
|
-
npcshrc_line = "\n# Source NPCSH configuration\nif [ -f ~/.npcshrc ]; then\n . ~/.npcshrc\nfi\n"
|
|
218
|
-
|
|
219
|
-
with open(config_file, "a+") as shell_config:
|
|
220
|
-
shell_config.seek(0)
|
|
221
|
-
content = shell_config.read()
|
|
222
|
-
if "source ~/.npcshrc" not in content and ". ~/.npcshrc" not in content:
|
|
223
|
-
shell_config.write(npcshrc_line)
|
|
224
|
-
print(f"Added .npcshrc sourcing to {config_file}")
|
|
225
|
-
else:
|
|
226
|
-
print(f".npcshrc already sourced in {config_file}")
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
def setup_npcsh_config() -> None:
|
|
230
|
-
"""
|
|
231
|
-
Function Description:
|
|
232
|
-
This function initializes the NPCSH configuration.
|
|
233
|
-
Args:
|
|
234
|
-
None
|
|
235
|
-
Keyword Args:
|
|
236
|
-
None
|
|
237
|
-
Returns:
|
|
238
|
-
None
|
|
239
|
-
"""
|
|
240
|
-
|
|
241
|
-
ensure_npcshrc_exists()
|
|
242
|
-
add_npcshrc_to_shell_config()
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
def is_npcsh_initialized() -> bool:
|
|
246
|
-
"""
|
|
247
|
-
Function Description:
|
|
248
|
-
This function checks if the NPCSH initialization flag is set.
|
|
249
|
-
Args:
|
|
250
|
-
None
|
|
251
|
-
Keyword Args:
|
|
252
|
-
None
|
|
253
|
-
Returns:
|
|
254
|
-
A boolean indicating whether NPCSH is initialized.
|
|
255
|
-
"""
|
|
256
|
-
|
|
257
|
-
return os.environ.get("NPCSH_INITIALIZED", None) == "1"
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
def set_npcsh_initialized() -> None:
|
|
261
|
-
"""
|
|
262
|
-
Function Description:
|
|
263
|
-
This function sets the NPCSH initialization flag in the .npcshrc file.
|
|
264
|
-
Args:
|
|
265
|
-
None
|
|
266
|
-
Keyword Args:
|
|
267
|
-
None
|
|
268
|
-
Returns:
|
|
269
|
-
|
|
270
|
-
None
|
|
271
|
-
"""
|
|
272
|
-
|
|
273
|
-
npcshrc_path = ensure_npcshrc_exists()
|
|
274
|
-
|
|
275
|
-
with open(npcshrc_path, "r+") as npcshrc:
|
|
276
|
-
content = npcshrc.read()
|
|
277
|
-
if "export NPCSH_INITIALIZED=0" in content:
|
|
278
|
-
content = content.replace(
|
|
279
|
-
"export NPCSH_INITIALIZED=0", "export NPCSH_INITIALIZED=1"
|
|
280
|
-
)
|
|
281
|
-
npcshrc.seek(0)
|
|
282
|
-
npcshrc.write(content)
|
|
283
|
-
npcshrc.truncate()
|
|
284
|
-
|
|
285
|
-
# Also set it for the current session
|
|
286
|
-
os.environ["NPCSH_INITIALIZED"] = "1"
|
|
287
|
-
print("NPCSH initialization flag set in .npcshrc")
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
def get_directory_npcs(directory: str = None) -> List[str]:
|
|
291
|
-
"""
|
|
292
|
-
Function Description:
|
|
293
|
-
This function retrieves a list of valid NPCs from the database.
|
|
294
|
-
Args:
|
|
295
|
-
db_path: The path to the database file.
|
|
296
|
-
Keyword Args:
|
|
297
|
-
None
|
|
298
|
-
Returns:
|
|
299
|
-
A list of valid NPCs.
|
|
300
|
-
"""
|
|
301
|
-
if directory is None:
|
|
302
|
-
directory = os.path.expanduser("./npc_team")
|
|
303
|
-
npcs = []
|
|
304
|
-
for filename in os.listdir(directory):
|
|
305
|
-
if filename.endswith(".npc"):
|
|
306
|
-
npcs.append(filename[:-4])
|
|
307
|
-
return npcs
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
def get_db_npcs(db_path: str) -> List[str]:
|
|
311
|
-
"""
|
|
312
|
-
Function Description:
|
|
313
|
-
This function retrieves a list of valid NPCs from the database.
|
|
314
|
-
Args:
|
|
315
|
-
db_path: The path to the database file.
|
|
316
|
-
Keyword Args:
|
|
317
|
-
None
|
|
318
|
-
Returns:
|
|
319
|
-
A list of valid NPCs.
|
|
320
|
-
"""
|
|
321
|
-
if "~" in db_path:
|
|
322
|
-
db_path = os.path.expanduser(db_path)
|
|
323
|
-
db_conn = sqlite3.connect(db_path)
|
|
324
|
-
cursor = db_conn.cursor()
|
|
325
|
-
cursor.execute("SELECT name FROM compiled_npcs")
|
|
326
|
-
npcs = [row[0] for row in cursor.fetchall()]
|
|
327
|
-
db_conn.close()
|
|
328
|
-
return npcs
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
def get_npc_path(npc_name: str, db_path: str) -> str:
|
|
332
|
-
# First, check in project npc_team directory
|
|
333
|
-
project_npc_team_dir = os.path.abspath("./npc_team")
|
|
334
|
-
project_npc_path = os.path.join(project_npc_team_dir, f"{npc_name}.npc")
|
|
335
|
-
|
|
336
|
-
# Then, check in global npc_team directory
|
|
337
|
-
user_npc_team_dir = os.path.expanduser("~/.npcsh/npc_team")
|
|
338
|
-
global_npc_path = os.path.join(user_npc_team_dir, f"{npc_name}.npc")
|
|
339
|
-
|
|
340
|
-
# Check database for compiled NPCs
|
|
341
|
-
try:
|
|
342
|
-
with sqlite3.connect(db_path) as conn:
|
|
343
|
-
cursor = conn.cursor()
|
|
344
|
-
query = f"SELECT source_path FROM compiled_npcs WHERE name = '{npc_name}'"
|
|
345
|
-
cursor.execute(query)
|
|
346
|
-
result = cursor.fetchone()
|
|
347
|
-
if result:
|
|
348
|
-
return result[0]
|
|
349
|
-
|
|
350
|
-
except Exception as e:
|
|
351
|
-
try:
|
|
352
|
-
with sqlite3.connect(db_path) as conn:
|
|
353
|
-
cursor = conn.cursor()
|
|
354
|
-
query = f"SELECT source_path FROM compiled_npcs WHERE name = {npc_name}"
|
|
355
|
-
cursor.execute(query)
|
|
356
|
-
result = cursor.fetchone()
|
|
357
|
-
if result:
|
|
358
|
-
return result[0]
|
|
359
|
-
except Exception as e:
|
|
360
|
-
print(f"Database query error: {e}")
|
|
361
|
-
|
|
362
|
-
# Fallback to file paths
|
|
363
|
-
if os.path.exists(project_npc_path):
|
|
364
|
-
return project_npc_path
|
|
365
|
-
|
|
366
|
-
if os.path.exists(global_npc_path):
|
|
367
|
-
return global_npc_path
|
|
368
|
-
|
|
369
|
-
raise ValueError(f"NPC file not found: {npc_name}")
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
def initialize_base_npcs_if_needed(db_path: str) -> None:
|
|
373
|
-
"""
|
|
374
|
-
Function Description:
|
|
375
|
-
This function initializes the base NPCs if they are not already in the database.
|
|
376
|
-
Args:
|
|
377
|
-
db_path: The path to the database file.
|
|
378
|
-
Keyword Args:
|
|
379
|
-
|
|
380
|
-
None
|
|
381
|
-
Returns:
|
|
382
|
-
None
|
|
383
|
-
"""
|
|
384
|
-
|
|
385
|
-
if is_npcsh_initialized():
|
|
386
|
-
return
|
|
387
|
-
|
|
388
|
-
conn = sqlite3.connect(db_path)
|
|
389
|
-
cursor = conn.cursor()
|
|
390
|
-
|
|
391
|
-
# Create the compiled_npcs table if it doesn't exist
|
|
392
|
-
cursor.execute(
|
|
393
|
-
"""
|
|
394
|
-
CREATE TABLE IF NOT EXISTS compiled_npcs (
|
|
395
|
-
name TEXT PRIMARY KEY,
|
|
396
|
-
source_path TEXT NOT NULL,
|
|
397
|
-
compiled_content TEXT
|
|
398
|
-
)
|
|
399
|
-
"""
|
|
400
|
-
)
|
|
401
|
-
|
|
402
|
-
# Get the path to the npc_team directory in the package
|
|
403
|
-
package_dir = os.path.dirname(__file__)
|
|
404
|
-
package_npc_team_dir = os.path.join(package_dir, "npc_team")
|
|
405
|
-
|
|
406
|
-
# User's global npc_team directory
|
|
407
|
-
user_npc_team_dir = os.path.expanduser("~/.npcsh/npc_team")
|
|
408
|
-
|
|
409
|
-
user_tools_dir = os.path.join(user_npc_team_dir, "tools")
|
|
410
|
-
user_templates_dir = os.path.join(user_npc_team_dir, "templates")
|
|
411
|
-
os.makedirs(user_npc_team_dir, exist_ok=True)
|
|
412
|
-
os.makedirs(user_tools_dir, exist_ok=True)
|
|
413
|
-
os.makedirs(user_templates_dir, exist_ok=True)
|
|
414
|
-
# Copy NPCs from package to user directory
|
|
415
|
-
for filename in os.listdir(package_npc_team_dir):
|
|
416
|
-
if filename.endswith(".npc"):
|
|
417
|
-
source_path = os.path.join(package_npc_team_dir, filename)
|
|
418
|
-
destination_path = os.path.join(user_npc_team_dir, filename)
|
|
419
|
-
if not os.path.exists(destination_path) or file_has_changed(
|
|
420
|
-
source_path, destination_path
|
|
421
|
-
):
|
|
422
|
-
shutil.copy2(source_path, destination_path)
|
|
423
|
-
|
|
424
|
-
# Copy tools from package to user directory
|
|
425
|
-
package_tools_dir = os.path.join(package_npc_team_dir, "tools")
|
|
426
|
-
if os.path.exists(package_tools_dir):
|
|
427
|
-
for filename in os.listdir(package_tools_dir):
|
|
428
|
-
if filename.endswith(".tool"):
|
|
429
|
-
source_tool_path = os.path.join(package_tools_dir, filename)
|
|
430
|
-
destination_tool_path = os.path.join(user_tools_dir, filename)
|
|
431
|
-
if (not os.path.exists(destination_tool_path)) or file_has_changed(
|
|
432
|
-
source_tool_path, destination_tool_path
|
|
433
|
-
):
|
|
434
|
-
shutil.copy2(source_tool_path, destination_tool_path)
|
|
435
|
-
print(f"Copied tool {filename} to {destination_tool_path}")
|
|
436
|
-
|
|
437
|
-
templates = os.path.join(package_npc_team_dir, "templates")
|
|
438
|
-
if os.path.exists(templates):
|
|
439
|
-
for folder in os.listdir(templates):
|
|
440
|
-
os.makedirs(os.path.join(user_templates_dir, folder), exist_ok=True)
|
|
441
|
-
for file in os.listdir(os.path.join(templates, folder)):
|
|
442
|
-
if file.endswith(".npc"):
|
|
443
|
-
source_template_path = os.path.join(templates, folder, file)
|
|
444
|
-
|
|
445
|
-
destination_template_path = os.path.join(
|
|
446
|
-
user_templates_dir, folder, file
|
|
447
|
-
)
|
|
448
|
-
if not os.path.exists(
|
|
449
|
-
destination_template_path
|
|
450
|
-
) or file_has_changed(
|
|
451
|
-
source_template_path, destination_template_path
|
|
452
|
-
):
|
|
453
|
-
shutil.copy2(source_template_path, destination_template_path)
|
|
454
|
-
print(f"Copied template {file} to {destination_template_path}")
|
|
455
|
-
conn.commit()
|
|
456
|
-
conn.close()
|
|
457
|
-
set_npcsh_initialized()
|
|
458
|
-
add_npcshrc_to_shell_config()
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
def file_has_changed(source_path: str, destination_path: str) -> bool:
|
|
462
|
-
"""
|
|
463
|
-
Function Description:
|
|
464
|
-
This function compares two files to determine if they are different.
|
|
465
|
-
Args:
|
|
466
|
-
source_path: The path to the source file.
|
|
467
|
-
destination_path: The path to the destination file.
|
|
468
|
-
Keyword Args:
|
|
469
|
-
None
|
|
470
|
-
Returns:
|
|
471
|
-
A boolean indicating whether the files are different
|
|
472
|
-
"""
|
|
473
|
-
|
|
474
|
-
# Compare file modification times or contents to decide whether to update the file
|
|
475
|
-
return not filecmp.cmp(source_path, destination_path, shallow=False)
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
def is_valid_npc(npc: str, db_path: str) -> bool:
|
|
479
|
-
"""
|
|
480
|
-
Function Description:
|
|
481
|
-
This function checks if an NPC is valid based on the database.
|
|
482
|
-
Args:
|
|
483
|
-
npc: The name of the NPC.
|
|
484
|
-
db_path: The path to the database file.
|
|
485
|
-
Keyword Args:
|
|
486
|
-
None
|
|
487
|
-
Returns:
|
|
488
|
-
A boolean indicating whether the NPC is valid.
|
|
489
|
-
"""
|
|
490
|
-
|
|
491
|
-
conn = sqlite3.connect(db_path)
|
|
492
|
-
cursor = conn.cursor()
|
|
493
|
-
cursor.execute("SELECT * FROM compiled_npcs WHERE name = ?", (npc,))
|
|
494
|
-
result = cursor.fetchone()
|
|
495
|
-
conn.close()
|
|
496
|
-
return result is not None
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
def execute_python(code: str) -> str:
|
|
500
|
-
"""
|
|
501
|
-
Function Description:
|
|
502
|
-
This function executes Python code and returns the output.
|
|
503
|
-
Args:
|
|
504
|
-
code: The Python code to execute.
|
|
505
|
-
Keyword Args:
|
|
506
|
-
None
|
|
507
|
-
Returns:
|
|
508
|
-
The output of the code execution.
|
|
509
|
-
"""
|
|
510
|
-
|
|
511
|
-
try:
|
|
512
|
-
result = subprocess.run(
|
|
513
|
-
["python", "-c", code], capture_output=True, text=True, timeout=30
|
|
514
|
-
)
|
|
515
|
-
return result.stdout if result.returncode == 0 else f"Error: {result.stderr}"
|
|
516
|
-
except subprocess.TimeoutExpired:
|
|
517
|
-
return "Error: Execution timed out"
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
def execute_r(code: str) -> str:
|
|
521
|
-
"""
|
|
522
|
-
Function Description:
|
|
523
|
-
This function executes R code and returns the output.
|
|
524
|
-
Args:
|
|
525
|
-
code: The R code to execute.
|
|
526
|
-
Keyword Args:
|
|
527
|
-
None
|
|
528
|
-
Returns:
|
|
529
|
-
The output of the code execution.
|
|
530
|
-
"""
|
|
531
|
-
|
|
532
|
-
try:
|
|
533
|
-
with tempfile.NamedTemporaryFile(
|
|
534
|
-
mode="w", suffix=".R", delete=False
|
|
535
|
-
) as temp_file:
|
|
536
|
-
temp_file.write(code)
|
|
537
|
-
temp_file_path = temp_file.name
|
|
538
|
-
|
|
539
|
-
result = subprocess.run(
|
|
540
|
-
["Rscript", temp_file_path], capture_output=True, text=True, timeout=30
|
|
541
|
-
)
|
|
542
|
-
os.unlink(temp_file_path)
|
|
543
|
-
return result.stdout if result.returncode == 0 else f"Error: {result.stderr}"
|
|
544
|
-
except subprocess.TimeoutExpired:
|
|
545
|
-
os.unlink(temp_file_path)
|
|
546
|
-
return "Error: Execution timed out"
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
def execute_sql(code: str) -> str:
|
|
550
|
-
"""
|
|
551
|
-
Function Description:
|
|
552
|
-
This function executes SQL code and returns the output.
|
|
553
|
-
Args:
|
|
554
|
-
code: The SQL code to execute.
|
|
555
|
-
Keyword Args:
|
|
556
|
-
None
|
|
557
|
-
Returns:
|
|
558
|
-
result: The output of the code execution.
|
|
559
|
-
"""
|
|
560
|
-
# use pandas to run the sql
|
|
561
|
-
try:
|
|
562
|
-
result = pd.read_sql_query(code, con=sqlite3.connect("npcsh_history.db"))
|
|
563
|
-
return result
|
|
564
|
-
except Exception as e:
|
|
565
|
-
return f"Error: {e}"
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
def list_directory(args: List[str]) -> None:
|
|
569
|
-
"""
|
|
570
|
-
Function Description:
|
|
571
|
-
This function lists the contents of a directory.
|
|
572
|
-
Args:
|
|
573
|
-
args: The command arguments.
|
|
574
|
-
Keyword Args:
|
|
575
|
-
None
|
|
576
|
-
Returns:
|
|
577
|
-
None
|
|
578
|
-
"""
|
|
579
|
-
directory = args[0] if args else "."
|
|
580
|
-
try:
|
|
581
|
-
files = os.listdir(directory)
|
|
582
|
-
for f in files:
|
|
583
|
-
print(f)
|
|
584
|
-
except Exception as e:
|
|
585
|
-
print(f"Error listing directory: {e}")
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
def read_file(args: List[str]) -> None:
|
|
589
|
-
"""
|
|
590
|
-
Function Description:
|
|
591
|
-
This function reads the contents of a file.
|
|
592
|
-
Args:
|
|
593
|
-
args: The command arguments.
|
|
594
|
-
Keyword Args:
|
|
595
|
-
None
|
|
596
|
-
Returns:
|
|
597
|
-
None
|
|
598
|
-
"""
|
|
599
|
-
|
|
600
|
-
if not args:
|
|
601
|
-
print("Usage: /read <filename>")
|
|
602
|
-
return
|
|
603
|
-
filename = args[0]
|
|
604
|
-
try:
|
|
605
|
-
with open(filename, "r") as file:
|
|
606
|
-
content = file.read()
|
|
607
|
-
print(content)
|
|
608
|
-
except Exception as e:
|
|
609
|
-
print(f"Error reading file: {e}")
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
import os
|
|
613
|
-
import json
|
|
614
|
-
from pathlib import Path
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
def get_npcshrc_path_windows():
|
|
618
|
-
return Path.home() / ".npcshrc"
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
def read_rc_file_windows(path):
|
|
622
|
-
"""Read shell-style rc file"""
|
|
623
|
-
config = {}
|
|
624
|
-
if not path.exists():
|
|
625
|
-
return config
|
|
626
|
-
|
|
627
|
-
with open(path) as f:
|
|
628
|
-
for line in f:
|
|
629
|
-
line = line.strip()
|
|
630
|
-
if line and not line.startswith("#"):
|
|
631
|
-
# Match KEY='value' or KEY="value" format
|
|
632
|
-
match = re.match(r'^([A-Z_]+)\s*=\s*[\'"](.*?)[\'"]$', line)
|
|
633
|
-
if match:
|
|
634
|
-
key, value = match.groups()
|
|
635
|
-
config[key] = value
|
|
636
|
-
return config
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
def get_setting_windows(key, default=None):
|
|
640
|
-
# Try environment variable first
|
|
641
|
-
if env_value := os.getenv(key):
|
|
642
|
-
return env_value
|
|
643
|
-
|
|
644
|
-
# Fall back to .npcshrc file
|
|
645
|
-
config = read_rc_file_windows(get_npcshrc_path())
|
|
646
|
-
return config.get(key, default)
|