npcsh 0.1.2__py3-none-any.whl → 1.1.13__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 +3508 -0
- npcsh/alicanto.py +65 -0
- npcsh/build.py +291 -0
- npcsh/completion.py +206 -0
- npcsh/config.py +163 -0
- npcsh/corca.py +50 -0
- npcsh/execution.py +185 -0
- npcsh/guac.py +46 -0
- npcsh/mcp_helpers.py +357 -0
- npcsh/mcp_server.py +299 -0
- npcsh/npc.py +323 -0
- npcsh/npc_team/alicanto.npc +2 -0
- npcsh/npc_team/alicanto.png +0 -0
- npcsh/npc_team/corca.npc +12 -0
- npcsh/npc_team/corca.png +0 -0
- npcsh/npc_team/corca_example.png +0 -0
- npcsh/npc_team/foreman.npc +7 -0
- npcsh/npc_team/frederic.npc +6 -0
- npcsh/npc_team/frederic4.png +0 -0
- npcsh/npc_team/guac.png +0 -0
- npcsh/npc_team/jinxs/code/python.jinx +11 -0
- npcsh/npc_team/jinxs/code/sh.jinx +34 -0
- npcsh/npc_team/jinxs/code/sql.jinx +16 -0
- npcsh/npc_team/jinxs/modes/alicanto.jinx +194 -0
- npcsh/npc_team/jinxs/modes/corca.jinx +249 -0
- npcsh/npc_team/jinxs/modes/guac.jinx +317 -0
- npcsh/npc_team/jinxs/modes/plonk.jinx +214 -0
- npcsh/npc_team/jinxs/modes/pti.jinx +170 -0
- npcsh/npc_team/jinxs/modes/spool.jinx +161 -0
- npcsh/npc_team/jinxs/modes/wander.jinx +186 -0
- npcsh/npc_team/jinxs/modes/yap.jinx +262 -0
- npcsh/npc_team/jinxs/npc_studio/npc-studio.jinx +77 -0
- npcsh/npc_team/jinxs/utils/agent.jinx +17 -0
- npcsh/npc_team/jinxs/utils/chat.jinx +44 -0
- npcsh/npc_team/jinxs/utils/cmd.jinx +44 -0
- npcsh/npc_team/jinxs/utils/compress.jinx +140 -0
- npcsh/npc_team/jinxs/utils/core/build.jinx +65 -0
- npcsh/npc_team/jinxs/utils/core/compile.jinx +50 -0
- npcsh/npc_team/jinxs/utils/core/help.jinx +52 -0
- npcsh/npc_team/jinxs/utils/core/init.jinx +41 -0
- npcsh/npc_team/jinxs/utils/core/jinxs.jinx +32 -0
- npcsh/npc_team/jinxs/utils/core/set.jinx +40 -0
- npcsh/npc_team/jinxs/utils/edit_file.jinx +94 -0
- npcsh/npc_team/jinxs/utils/load_file.jinx +35 -0
- npcsh/npc_team/jinxs/utils/ots.jinx +61 -0
- npcsh/npc_team/jinxs/utils/roll.jinx +68 -0
- npcsh/npc_team/jinxs/utils/sample.jinx +56 -0
- npcsh/npc_team/jinxs/utils/search.jinx +130 -0
- npcsh/npc_team/jinxs/utils/serve.jinx +26 -0
- npcsh/npc_team/jinxs/utils/sleep.jinx +116 -0
- npcsh/npc_team/jinxs/utils/trigger.jinx +61 -0
- npcsh/npc_team/jinxs/utils/usage.jinx +33 -0
- npcsh/npc_team/jinxs/utils/vixynt.jinx +144 -0
- npcsh/npc_team/kadiefa.npc +3 -0
- npcsh/npc_team/kadiefa.png +0 -0
- npcsh/npc_team/npcsh.ctx +18 -0
- npcsh/npc_team/npcsh_sibiji.png +0 -0
- npcsh/npc_team/plonk.npc +2 -0
- npcsh/npc_team/plonk.png +0 -0
- npcsh/npc_team/plonkjr.npc +2 -0
- npcsh/npc_team/plonkjr.png +0 -0
- npcsh/npc_team/sibiji.npc +3 -0
- npcsh/npc_team/sibiji.png +0 -0
- npcsh/npc_team/spool.png +0 -0
- npcsh/npc_team/yap.png +0 -0
- npcsh/npcsh.py +296 -112
- npcsh/parsing.py +118 -0
- npcsh/plonk.py +54 -0
- npcsh/pti.py +54 -0
- npcsh/routes.py +139 -0
- npcsh/spool.py +48 -0
- npcsh/ui.py +199 -0
- npcsh/wander.py +62 -0
- npcsh/yap.py +50 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/agent.jinx +17 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/alicanto.jinx +194 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/alicanto.npc +2 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/alicanto.png +0 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/build.jinx +65 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/chat.jinx +44 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/cmd.jinx +44 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/compile.jinx +50 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/compress.jinx +140 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/corca.jinx +249 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/corca.npc +12 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/corca.png +0 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/corca_example.png +0 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/edit_file.jinx +94 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/foreman.npc +7 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/frederic.npc +6 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/frederic4.png +0 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/guac.jinx +317 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/guac.png +0 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/help.jinx +52 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/init.jinx +41 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/jinxs.jinx +32 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/kadiefa.npc +3 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/kadiefa.png +0 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/load_file.jinx +35 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/npc-studio.jinx +77 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/npcsh.ctx +18 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/ots.jinx +61 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/plonk.jinx +214 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/plonk.npc +2 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/plonk.png +0 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/plonkjr.npc +2 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/plonkjr.png +0 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/pti.jinx +170 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/python.jinx +11 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/roll.jinx +68 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/sample.jinx +56 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/search.jinx +130 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/serve.jinx +26 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/set.jinx +40 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/sh.jinx +34 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/sibiji.npc +3 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/sibiji.png +0 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/sleep.jinx +116 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/spool.jinx +161 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/spool.png +0 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/sql.jinx +16 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/trigger.jinx +61 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/usage.jinx +33 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/vixynt.jinx +144 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/wander.jinx +186 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/yap.jinx +262 -0
- npcsh-1.1.13.data/data/npcsh/npc_team/yap.png +0 -0
- npcsh-1.1.13.dist-info/METADATA +522 -0
- npcsh-1.1.13.dist-info/RECORD +135 -0
- {npcsh-0.1.2.dist-info → npcsh-1.1.13.dist-info}/WHEEL +1 -1
- npcsh-1.1.13.dist-info/entry_points.txt +9 -0
- {npcsh-0.1.2.dist-info → npcsh-1.1.13.dist-info/licenses}/LICENSE +1 -1
- npcsh/command_history.py +0 -81
- npcsh/helpers.py +0 -36
- npcsh/llm_funcs.py +0 -295
- npcsh/main.py +0 -5
- npcsh/modes.py +0 -343
- npcsh/npc_compiler.py +0 -124
- npcsh-0.1.2.dist-info/METADATA +0 -99
- npcsh-0.1.2.dist-info/RECORD +0 -14
- npcsh-0.1.2.dist-info/entry_points.txt +0 -2
- {npcsh-0.1.2.dist-info → npcsh-1.1.13.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
jinx_name: corca
|
|
2
|
+
description: MCP-powered agentic shell - LLM with tool use via MCP servers
|
|
3
|
+
inputs:
|
|
4
|
+
- mcp_server_path: null
|
|
5
|
+
- initial_command: null
|
|
6
|
+
- model: null
|
|
7
|
+
- provider: null
|
|
8
|
+
|
|
9
|
+
steps:
|
|
10
|
+
- name: corca_repl
|
|
11
|
+
engine: python
|
|
12
|
+
code: |
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import asyncio
|
|
16
|
+
import json
|
|
17
|
+
from contextlib import AsyncExitStack
|
|
18
|
+
from termcolor import colored
|
|
19
|
+
|
|
20
|
+
from npcpy.llm_funcs import get_llm_response
|
|
21
|
+
from npcpy.npc_sysenv import render_markdown, get_system_message
|
|
22
|
+
|
|
23
|
+
# MCP imports
|
|
24
|
+
try:
|
|
25
|
+
from mcp import ClientSession, StdioServerParameters
|
|
26
|
+
from mcp.client.stdio import stdio_client
|
|
27
|
+
MCP_AVAILABLE = True
|
|
28
|
+
except ImportError:
|
|
29
|
+
MCP_AVAILABLE = False
|
|
30
|
+
print(colored("MCP not available. Install with: pip install mcp-client", "yellow"))
|
|
31
|
+
|
|
32
|
+
npc = context.get('npc')
|
|
33
|
+
team = context.get('team')
|
|
34
|
+
messages = context.get('messages', [])
|
|
35
|
+
mcp_server_path = context.get('mcp_server_path')
|
|
36
|
+
initial_command = context.get('initial_command')
|
|
37
|
+
|
|
38
|
+
model = context.get('model') or (npc.model if npc else None)
|
|
39
|
+
provider = context.get('provider') or (npc.provider if npc else None)
|
|
40
|
+
|
|
41
|
+
# Use shared_context for MCP state
|
|
42
|
+
shared_ctx = npc.shared_context if npc and hasattr(npc, 'shared_context') else {}
|
|
43
|
+
|
|
44
|
+
print("""
|
|
45
|
+
██████╗ ██████╗ ██████╗ ██████╗ █████╗
|
|
46
|
+
██╔════╝██╔═══██╗██╔══██╗██╔════╝██╔══██╗
|
|
47
|
+
██║ ██║ ██║██████╔╝██║ ███████║
|
|
48
|
+
██║ ██║ ██║██╔══██╗██║ ██╔══██╗
|
|
49
|
+
╚██████╗╚██████╔╝██║ ██║╚██████╗██║ ██║
|
|
50
|
+
╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
|
|
51
|
+
""")
|
|
52
|
+
|
|
53
|
+
npc_name = npc.name if npc else "corca"
|
|
54
|
+
print(f"Entering corca mode (NPC: {npc_name}). Type '/cq' to exit.")
|
|
55
|
+
|
|
56
|
+
# ========== MCP Connection Setup ==========
|
|
57
|
+
async def connect_mcp(server_path):
|
|
58
|
+
"""Connect to MCP server and return tools"""
|
|
59
|
+
if not MCP_AVAILABLE:
|
|
60
|
+
return [], {}
|
|
61
|
+
|
|
62
|
+
abs_path = os.path.abspath(os.path.expanduser(server_path))
|
|
63
|
+
if not os.path.exists(abs_path):
|
|
64
|
+
print(colored(f"MCP server not found: {abs_path}", "red"))
|
|
65
|
+
return [], {}
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
loop = asyncio.get_event_loop()
|
|
69
|
+
except RuntimeError:
|
|
70
|
+
loop = asyncio.new_event_loop()
|
|
71
|
+
asyncio.set_event_loop(loop)
|
|
72
|
+
|
|
73
|
+
exit_stack = AsyncExitStack()
|
|
74
|
+
|
|
75
|
+
if abs_path.endswith('.py'):
|
|
76
|
+
cmd_parts = [sys.executable, abs_path]
|
|
77
|
+
else:
|
|
78
|
+
cmd_parts = [abs_path]
|
|
79
|
+
|
|
80
|
+
server_params = StdioServerParameters(
|
|
81
|
+
command=cmd_parts[0],
|
|
82
|
+
args=[abs_path],
|
|
83
|
+
env=os.environ.copy()
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
stdio_transport = await exit_stack.enter_async_context(stdio_client(server_params))
|
|
87
|
+
session = await exit_stack.enter_async_context(ClientSession(*stdio_transport))
|
|
88
|
+
await session.initialize()
|
|
89
|
+
|
|
90
|
+
response = await session.list_tools()
|
|
91
|
+
tools_llm = []
|
|
92
|
+
tool_map = {}
|
|
93
|
+
|
|
94
|
+
if response.tools:
|
|
95
|
+
for mcp_tool in response.tools:
|
|
96
|
+
tool_def = {
|
|
97
|
+
"type": "function",
|
|
98
|
+
"function": {
|
|
99
|
+
"name": mcp_tool.name,
|
|
100
|
+
"description": mcp_tool.description or f"MCP tool: {mcp_tool.name}",
|
|
101
|
+
"parameters": getattr(mcp_tool, "inputSchema", {"type": "object", "properties": {}})
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
tools_llm.append(tool_def)
|
|
105
|
+
|
|
106
|
+
# Create sync wrapper for async tool call
|
|
107
|
+
def make_tool_func(tool_name, sess, lp):
|
|
108
|
+
async def call_tool(**kwargs):
|
|
109
|
+
cleaned = {k: (None if v == 'None' else v) for k, v in kwargs.items()}
|
|
110
|
+
result = await asyncio.wait_for(sess.call_tool(tool_name, cleaned), timeout=30.0)
|
|
111
|
+
return result
|
|
112
|
+
def sync_call(**kwargs):
|
|
113
|
+
return lp.run_until_complete(call_tool(**kwargs))
|
|
114
|
+
return sync_call
|
|
115
|
+
|
|
116
|
+
tool_map[mcp_tool.name] = make_tool_func(mcp_tool.name, session, loop)
|
|
117
|
+
|
|
118
|
+
# Store in shared context
|
|
119
|
+
shared_ctx['mcp_client'] = session
|
|
120
|
+
shared_ctx['mcp_tools'] = tools_llm
|
|
121
|
+
shared_ctx['mcp_tool_map'] = tool_map
|
|
122
|
+
shared_ctx['_mcp_exit_stack'] = exit_stack
|
|
123
|
+
shared_ctx['_mcp_loop'] = loop
|
|
124
|
+
|
|
125
|
+
print(colored(f"Connected to MCP server. Tools: {', '.join(tool_map.keys())}", "green"))
|
|
126
|
+
return tools_llm, tool_map
|
|
127
|
+
|
|
128
|
+
# Try to connect if server path provided
|
|
129
|
+
tools_llm = shared_ctx.get('mcp_tools', [])
|
|
130
|
+
tool_map = shared_ctx.get('mcp_tool_map', {})
|
|
131
|
+
|
|
132
|
+
if mcp_server_path and not tools_llm:
|
|
133
|
+
try:
|
|
134
|
+
loop = asyncio.get_event_loop()
|
|
135
|
+
except RuntimeError:
|
|
136
|
+
loop = asyncio.new_event_loop()
|
|
137
|
+
asyncio.set_event_loop(loop)
|
|
138
|
+
tools_llm, tool_map = loop.run_until_complete(connect_mcp(mcp_server_path))
|
|
139
|
+
|
|
140
|
+
# Find default MCP server if none provided
|
|
141
|
+
if not tools_llm:
|
|
142
|
+
default_paths = [
|
|
143
|
+
os.path.expanduser("~/.npcsh/npc_team/mcp_server.py"),
|
|
144
|
+
os.path.join(team.team_path, "mcp_server.py") if team and hasattr(team, 'team_path') else None,
|
|
145
|
+
]
|
|
146
|
+
for path in default_paths:
|
|
147
|
+
if path and os.path.exists(path):
|
|
148
|
+
try:
|
|
149
|
+
loop = asyncio.get_event_loop()
|
|
150
|
+
except RuntimeError:
|
|
151
|
+
loop = asyncio.new_event_loop()
|
|
152
|
+
asyncio.set_event_loop(loop)
|
|
153
|
+
tools_llm, tool_map = loop.run_until_complete(connect_mcp(path))
|
|
154
|
+
if tools_llm:
|
|
155
|
+
break
|
|
156
|
+
|
|
157
|
+
# Ensure system message
|
|
158
|
+
if not messages or messages[0].get("role") != "system":
|
|
159
|
+
sys_msg = get_system_message(npc) if npc else "You are an AI assistant with access to tools."
|
|
160
|
+
if tools_llm:
|
|
161
|
+
sys_msg += f"\n\nYou have access to these tools: {', '.join(t['function']['name'] for t in tools_llm)}"
|
|
162
|
+
messages.insert(0, {"role": "system", "content": sys_msg})
|
|
163
|
+
|
|
164
|
+
# Handle initial command if provided (one-shot mode)
|
|
165
|
+
if initial_command:
|
|
166
|
+
resp = get_llm_response(
|
|
167
|
+
initial_command,
|
|
168
|
+
model=model,
|
|
169
|
+
provider=provider,
|
|
170
|
+
messages=messages,
|
|
171
|
+
tools=tools_llm if tools_llm else None,
|
|
172
|
+
tool_map=tool_map if tool_map else None,
|
|
173
|
+
auto_process_tool_calls=True,
|
|
174
|
+
npc=npc
|
|
175
|
+
)
|
|
176
|
+
messages = resp.get('messages', messages)
|
|
177
|
+
render_markdown(str(resp.get('response', '')))
|
|
178
|
+
context['output'] = resp.get('response', 'Done.')
|
|
179
|
+
context['messages'] = messages
|
|
180
|
+
# Don't enter REPL for one-shot
|
|
181
|
+
exit()
|
|
182
|
+
|
|
183
|
+
# REPL loop
|
|
184
|
+
while True:
|
|
185
|
+
try:
|
|
186
|
+
prompt_str = f"{npc_name}:corca> "
|
|
187
|
+
user_input = input(prompt_str).strip()
|
|
188
|
+
|
|
189
|
+
if not user_input:
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
if user_input.lower() == "/cq":
|
|
193
|
+
print("Exiting corca mode.")
|
|
194
|
+
break
|
|
195
|
+
|
|
196
|
+
# Handle /tools to list available tools
|
|
197
|
+
if user_input.lower() == "/tools":
|
|
198
|
+
if tools_llm:
|
|
199
|
+
print(colored("Available MCP tools:", "cyan"))
|
|
200
|
+
for t in tools_llm:
|
|
201
|
+
print(f" - {t['function']['name']}: {t['function'].get('description', '')[:60]}")
|
|
202
|
+
else:
|
|
203
|
+
print(colored("No MCP tools connected.", "yellow"))
|
|
204
|
+
continue
|
|
205
|
+
|
|
206
|
+
# Handle /connect to connect to new MCP server
|
|
207
|
+
if user_input.startswith("/connect "):
|
|
208
|
+
new_path = user_input[9:].strip()
|
|
209
|
+
try:
|
|
210
|
+
loop = asyncio.get_event_loop()
|
|
211
|
+
except RuntimeError:
|
|
212
|
+
loop = asyncio.new_event_loop()
|
|
213
|
+
asyncio.set_event_loop(loop)
|
|
214
|
+
tools_llm, tool_map = loop.run_until_complete(connect_mcp(new_path))
|
|
215
|
+
continue
|
|
216
|
+
|
|
217
|
+
# Get LLM response with tools
|
|
218
|
+
resp = get_llm_response(
|
|
219
|
+
user_input,
|
|
220
|
+
model=model,
|
|
221
|
+
provider=provider,
|
|
222
|
+
messages=messages,
|
|
223
|
+
tools=tools_llm if tools_llm else None,
|
|
224
|
+
tool_map=tool_map if tool_map else None,
|
|
225
|
+
auto_process_tool_calls=True,
|
|
226
|
+
stream=False, # Tool calls don't work well with streaming
|
|
227
|
+
npc=npc
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
messages = resp.get('messages', messages)
|
|
231
|
+
response_text = resp.get('response', '')
|
|
232
|
+
render_markdown(str(response_text))
|
|
233
|
+
|
|
234
|
+
# Track usage
|
|
235
|
+
if 'usage' in resp and npc and hasattr(npc, 'shared_context'):
|
|
236
|
+
usage = resp['usage']
|
|
237
|
+
npc.shared_context['session_input_tokens'] += usage.get('input_tokens', 0)
|
|
238
|
+
npc.shared_context['session_output_tokens'] += usage.get('output_tokens', 0)
|
|
239
|
+
npc.shared_context['turn_count'] += 1
|
|
240
|
+
|
|
241
|
+
except KeyboardInterrupt:
|
|
242
|
+
print("\nUse '/cq' to exit or continue.")
|
|
243
|
+
continue
|
|
244
|
+
except EOFError:
|
|
245
|
+
print("\nExiting corca mode.")
|
|
246
|
+
break
|
|
247
|
+
|
|
248
|
+
context['output'] = "Exited corca mode."
|
|
249
|
+
context['messages'] = messages
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
name: corca
|
|
2
|
+
primary_directive: |
|
|
3
|
+
You are corca, a distinguished member of the NPC team.
|
|
4
|
+
Your expertise is in the area of software development and
|
|
5
|
+
you have a knack for thinking through problems carefully.
|
|
6
|
+
You favor solutions that prioritize simplicity and clarity and
|
|
7
|
+
ought to always consider how some suggestion may increase rather than reduce tech debt
|
|
8
|
+
unnecessarily. Now, the key is in this last term, "unnecessarily".
|
|
9
|
+
You must distinguish carefully and when in doubt, opt to ask for further
|
|
10
|
+
information or clarification with concrete clear options that make it
|
|
11
|
+
easy for a user to choose.
|
|
12
|
+
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
jinx_name: edit_file
|
|
2
|
+
description: Examines a file, determines what changes are needed, and applies those
|
|
3
|
+
changes.
|
|
4
|
+
inputs:
|
|
5
|
+
- file_path
|
|
6
|
+
- edit_instructions
|
|
7
|
+
- backup: true
|
|
8
|
+
steps:
|
|
9
|
+
- name: "edit_file"
|
|
10
|
+
engine: "python"
|
|
11
|
+
code: |
|
|
12
|
+
import os
|
|
13
|
+
from npcpy.llm_funcs import get_llm_response
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
file_path = os.path.expanduser("{{ file_path }}")
|
|
17
|
+
edit_instructions = "{{ edit_instructions }}"
|
|
18
|
+
backup_str = "{{ backup }}"
|
|
19
|
+
create_backup = backup_str.lower() not in ('false', 'no', '0', '')
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
with open(file_path, 'r') as f:
|
|
23
|
+
original_content = f.read()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if create_backup:
|
|
27
|
+
backup_path = file_path + ".bak"
|
|
28
|
+
with open(backup_path, 'w') as f:
|
|
29
|
+
f.write(original_content)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
prompt = """You are a code editing assistant. Analyze this file and make the requested changes.
|
|
33
|
+
|
|
34
|
+
File content:
|
|
35
|
+
""" + original_content + """
|
|
36
|
+
|
|
37
|
+
Edit instructions: """ + edit_instructions + """
|
|
38
|
+
|
|
39
|
+
Return a JSON object with these fields:
|
|
40
|
+
1. "modifications": An array of modification objects, where each object has:
|
|
41
|
+
- "type": One of "replace", "insert_after", "insert_before", or "delete"
|
|
42
|
+
- "target": For "insert_after" and "insert_before", the text to insert after/before
|
|
43
|
+
For "delete", the text to delete
|
|
44
|
+
- "original": For "replace", the text to be replaced
|
|
45
|
+
- "replacement": For "replace", the text to replace with
|
|
46
|
+
- "insertion": For "insert_after" and "insert_before", the text to insert
|
|
47
|
+
2. "explanation": Brief explanation of the changes made
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
response = get_llm_response(prompt, model=npc.model, provider=npc.provider, npc=npc, format="json")
|
|
51
|
+
|
|
52
|
+
result = response.get("response", {})
|
|
53
|
+
modifications = result.get("modifications", [])
|
|
54
|
+
explanation = result.get("explanation", "No explanation provided")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
updated_content = original_content
|
|
58
|
+
changes_applied = 0
|
|
59
|
+
|
|
60
|
+
for mod in modifications:
|
|
61
|
+
print(mod)
|
|
62
|
+
mod_type = mod.get("type")
|
|
63
|
+
|
|
64
|
+
if mod_type == "replace":
|
|
65
|
+
original = mod.get("original")
|
|
66
|
+
replacement = mod.get("replacement")
|
|
67
|
+
if original in updated_content:
|
|
68
|
+
updated_content = updated_content.replace(original, replacement)
|
|
69
|
+
changes_applied += 1
|
|
70
|
+
|
|
71
|
+
elif mod_type == "insert_after":
|
|
72
|
+
target = mod.get("target")
|
|
73
|
+
insertion = mod.get("insertion")
|
|
74
|
+
if target in updated_content:
|
|
75
|
+
updated_content = updated_content.replace(target, target + insertion)
|
|
76
|
+
changes_applied += 1
|
|
77
|
+
|
|
78
|
+
elif mod_type == "insert_before":
|
|
79
|
+
target = mod.get("target")
|
|
80
|
+
insertion = mod.get("insertion")
|
|
81
|
+
if target in updated_content:
|
|
82
|
+
updated_content = updated_content.replace(target, insertion + target)
|
|
83
|
+
changes_applied += 1
|
|
84
|
+
|
|
85
|
+
elif mod_type == "delete":
|
|
86
|
+
target = mod.get("target")
|
|
87
|
+
if target in updated_content:
|
|
88
|
+
updated_content = updated_content.replace(target, "")
|
|
89
|
+
changes_applied += 1
|
|
90
|
+
|
|
91
|
+
with open(file_path, 'w') as f:
|
|
92
|
+
f.write(updated_content)
|
|
93
|
+
|
|
94
|
+
output = "Applied " + str(changes_applied) + " changes to " + file_path + "\n\n" + explanation
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
name: foreman
|
|
2
|
+
primary_directive: You are the foreman of an NPC team. It is your duty
|
|
3
|
+
to delegate tasks to your team members or to other specialized teams
|
|
4
|
+
in order to complete the project. You are responsible for the
|
|
5
|
+
completion of the project and the safety of your team members.
|
|
6
|
+
model: gpt-4o-mini
|
|
7
|
+
provider: openai
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
name: frederic
|
|
2
|
+
primary_directive: |
|
|
3
|
+
You are frederic the polar bear. Your job is help users think through problems and
|
|
4
|
+
to provide straightforward ways forward on problems. Cut through the ice
|
|
5
|
+
to get to what matters and keep things simple. You are to respond in a
|
|
6
|
+
witty tone like richard feynman but with the romantic tambor of Frederic Chopin.
|
|
Binary file
|