npcsh 1.1.18__py3-none-any.whl → 1.1.20__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 +19 -7
- npcsh/benchmark/npcsh_agent.py +47 -16
- npcsh/config.py +1 -0
- npcsh/diff_viewer.py +452 -0
- npcsh/npc_team/jinxs/bin/config_tui.jinx +300 -0
- npcsh/npc_team/jinxs/bin/jinxs.jinx +407 -0
- npcsh/npc_team/jinxs/bin/kg.jinx +941 -0
- npcsh/npc_team/jinxs/bin/memories.jinx +317 -0
- npcsh/npc_team/jinxs/bin/models.jinx +343 -0
- npcsh/npc_team/jinxs/bin/nql.jinx +380 -50
- npcsh/npc_team/jinxs/bin/setup.jinx +241 -0
- npcsh/npc_team/jinxs/bin/sync.jinx +143 -150
- npcsh/npc_team/jinxs/bin/team.jinx +504 -0
- npcsh/npc_team/jinxs/incognide/add_tab.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/close_pane.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/close_tab.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/confirm.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/focus_pane.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/list_panes.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/navigate.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/notify.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/open_pane.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/read_pane.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/run_terminal.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/send_message.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/split_pane.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/switch_npc.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/switch_tab.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/write_file.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/zen_mode.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +1 -1
- npcsh/npc_team/jinxs/lib/research/paper_search.jinx +1 -1
- npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +1 -1
- npcsh/npc_team/jinxs/modes/alicanto.jinx +1 -1
- npcsh/npc_team/jinxs/modes/arxiv.jinx +1 -1
- npcsh/npc_team/jinxs/modes/corca.jinx +1 -1
- npcsh/npc_team/jinxs/modes/guac.jinx +4 -6
- npcsh/npc_team/jinxs/modes/plonk.jinx +1 -1
- npcsh/npc_team/jinxs/modes/pti.jinx +1 -1
- npcsh/npc_team/jinxs/modes/reattach.jinx +1 -1
- npcsh/npc_team/jinxs/modes/spool.jinx +1 -1
- npcsh/npc_team/jinxs/modes/wander.jinx +1 -1
- npcsh/routes.py +8 -2
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/add_tab.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/alicanto.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/arxiv.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/close_pane.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/close_tab.jinx +1 -1
- npcsh-1.1.20.data/data/npcsh/npc_team/config_tui.jinx +300 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/confirm.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/corca.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/db_search.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/file_search.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/focus_pane.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/guac.jinx +4 -6
- npcsh-1.1.20.data/data/npcsh/npc_team/jinxs.jinx +407 -0
- npcsh-1.1.20.data/data/npcsh/npc_team/kg.jinx +941 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/kg_search.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/list_panes.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/mem_search.jinx +1 -1
- npcsh-1.1.20.data/data/npcsh/npc_team/memories.jinx +317 -0
- npcsh-1.1.20.data/data/npcsh/npc_team/models.jinx +343 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/navigate.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/notify.jinx +1 -1
- npcsh-1.1.20.data/data/npcsh/npc_team/nql.jinx +471 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/open_pane.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/paper_search.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/plonk.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/pti.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/read_pane.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/reattach.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/run_terminal.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/semantic_scholar.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/send_message.jinx +1 -1
- npcsh-1.1.20.data/data/npcsh/npc_team/setup.jinx +241 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/split_pane.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/spool.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/switch_npc.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/switch_tab.jinx +1 -1
- npcsh-1.1.20.data/data/npcsh/npc_team/sync.jinx +223 -0
- npcsh-1.1.20.data/data/npcsh/npc_team/team.jinx +504 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/wander.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/web_search.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/write_file.jinx +1 -1
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/zen_mode.jinx +1 -1
- {npcsh-1.1.18.dist-info → npcsh-1.1.20.dist-info}/METADATA +21 -14
- npcsh-1.1.20.dist-info/RECORD +248 -0
- {npcsh-1.1.18.dist-info → npcsh-1.1.20.dist-info}/entry_points.txt +7 -0
- npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +0 -331
- npcsh-1.1.18.data/data/npcsh/npc_team/jinxs.jinx +0 -331
- npcsh-1.1.18.data/data/npcsh/npc_team/nql.jinx +0 -141
- npcsh-1.1.18.data/data/npcsh/npc_team/sync.jinx +0 -230
- npcsh-1.1.18.dist-info/RECORD +0 -235
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/alicanto.npc +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/benchmark.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/build.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/chat.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/click.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/cmd.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/compile.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/compress.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/convene.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/corca.npc +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/corca_example.png +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/delegate.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/edit_file.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/frederic.npc +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/guac.npc +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/help.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/incognide.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/init.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/key_press.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/load_file.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/mem_review.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/ots.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/paste.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/plonk.npc +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/plonkjr.npc +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/python.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/roll.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/sample.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/search.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/serve.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/set.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/sh.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/shh.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/sibiji.npc +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/sleep.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/sql.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/switch.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/switches.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/trigger.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/type_text.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/usage.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/verbose.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/vixynt.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/wait.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/yap.jinx +0 -0
- {npcsh-1.1.18.data → npcsh-1.1.20.data}/data/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.1.18.dist-info → npcsh-1.1.20.dist-info}/WHEEL +0 -0
- {npcsh-1.1.18.dist-info → npcsh-1.1.20.dist-info}/licenses/LICENSE +0 -0
- {npcsh-1.1.18.dist-info → npcsh-1.1.20.dist-info}/top_level.txt +0 -0
|
@@ -114,7 +114,7 @@ steps:
|
|
|
114
114
|
|
|
115
115
|
line = str(items[idx])[:width-2]
|
|
116
116
|
if current_tab in [0, 1, 2] and idx == selected:
|
|
117
|
-
sys.stdout.write(f'\033[
|
|
117
|
+
sys.stdout.write(f'\033[7;1m>{line.ljust(width-2)}\033[0m')
|
|
118
118
|
else:
|
|
119
119
|
# Color gold/cliff markers
|
|
120
120
|
if '[GOLD]' in line:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
jinx_name: guac
|
|
2
|
-
description: Interactive Python
|
|
2
|
+
description: Interactive Python TUI - live variable inspector, code execution, DataFrame viewer
|
|
3
3
|
inputs:
|
|
4
4
|
- model: null
|
|
5
5
|
- provider: null
|
|
@@ -24,8 +24,6 @@ steps:
|
|
|
24
24
|
|
|
25
25
|
import numpy as np
|
|
26
26
|
import pandas as pd
|
|
27
|
-
import matplotlib
|
|
28
|
-
matplotlib.use('Agg') # Non-interactive backend for TUI
|
|
29
27
|
import matplotlib.pyplot as plt
|
|
30
28
|
|
|
31
29
|
from npcpy.llm_funcs import get_llm_response
|
|
@@ -204,7 +202,7 @@ steps:
|
|
|
204
202
|
# ===== HEADER =====
|
|
205
203
|
mode_colors = {"code": "\033[32m", "natural": "\033[35m", "inspect": "\033[33m"}
|
|
206
204
|
mode_str = f"{mode_colors[state.mode]}[{state.mode}]\033[0m"
|
|
207
|
-
header = f" GUAC - Python
|
|
205
|
+
header = f" GUAC - Interactive Python {mode_str} "
|
|
208
206
|
status_color = "\033[33m" if "..." in state.status else "\033[32m"
|
|
209
207
|
out.append(f"\033[1;1H\033[42;30;1m{header.ljust(width)}\033[0m")
|
|
210
208
|
out.append(f"\033[1;{width-len(state.status)-3}H{status_color}[{state.status}]\033[0m")
|
|
@@ -277,7 +275,7 @@ steps:
|
|
|
277
275
|
tabs = ""
|
|
278
276
|
for i, name in enumerate(panel_names):
|
|
279
277
|
if i == state.panel:
|
|
280
|
-
tabs += f"\033[
|
|
278
|
+
tabs += f"\033[7m {name} \033[0m"
|
|
281
279
|
else:
|
|
282
280
|
tabs += f"\033[90m {name} \033[0m"
|
|
283
281
|
out.append(f"\033[3;{right_x}H{tabs}")
|
|
@@ -293,7 +291,7 @@ steps:
|
|
|
293
291
|
info = var_info(name, value)
|
|
294
292
|
display = f"{name[:10]:<10} {info[:rpanel_w-12]}"
|
|
295
293
|
if idx == state.selected_var:
|
|
296
|
-
out.append(f"\033[{4+i};{right_x}H\033[
|
|
294
|
+
out.append(f"\033[{4+i};{right_x}H\033[7m>{display[:rpanel_w]}\033[0m")
|
|
297
295
|
elif isinstance(value, pd.DataFrame):
|
|
298
296
|
out.append(f"\033[{4+i};{right_x}H\033[34m {display[:rpanel_w]}\033[0m")
|
|
299
297
|
elif isinstance(value, np.ndarray):
|
|
@@ -130,7 +130,7 @@ steps:
|
|
|
130
130
|
color = ''
|
|
131
131
|
|
|
132
132
|
if idx == selected:
|
|
133
|
-
sys.stdout.write(f'\033[
|
|
133
|
+
sys.stdout.write(f'\033[7;1m>{line.ljust(width-2)}\033[0m')
|
|
134
134
|
elif color:
|
|
135
135
|
sys.stdout.write(f'{color}{line}\033[0m')
|
|
136
136
|
else:
|
|
@@ -123,7 +123,7 @@ steps:
|
|
|
123
123
|
line = line[:width-1]
|
|
124
124
|
|
|
125
125
|
if idx == selected:
|
|
126
|
-
sys.stdout.write(f'\033[
|
|
126
|
+
sys.stdout.write(f'\033[7;1m>{line.ljust(width-2)}\033[0m')
|
|
127
127
|
else:
|
|
128
128
|
sys.stdout.write(f'\033[33m{line}\033[0m')
|
|
129
129
|
|
|
@@ -118,7 +118,7 @@ steps:
|
|
|
118
118
|
color = ''
|
|
119
119
|
|
|
120
120
|
if idx == selected:
|
|
121
|
-
sys.stdout.write(f'\033[
|
|
121
|
+
sys.stdout.write(f'\033[7;1m>{line.ljust(width-2)}\033[0m')
|
|
122
122
|
elif color:
|
|
123
123
|
sys.stdout.write(f'{color}{line}\033[0m')
|
|
124
124
|
else:
|
|
@@ -217,7 +217,7 @@ steps:
|
|
|
217
217
|
panel_tabs = ""
|
|
218
218
|
for i, name in enumerate(["Output", "Streams", "Starred"]):
|
|
219
219
|
if i == state.current_panel:
|
|
220
|
-
panel_tabs += f"\033[
|
|
220
|
+
panel_tabs += f"\033[7m {name} \033[0m "
|
|
221
221
|
else:
|
|
222
222
|
panel_tabs += f"\033[90m {name} \033[0m "
|
|
223
223
|
|
npcsh/routes.py
CHANGED
|
@@ -12,6 +12,7 @@ class CommandRouter:
|
|
|
12
12
|
self.routes = {}
|
|
13
13
|
self.help_info = {}
|
|
14
14
|
self.jinx_routes = {}
|
|
15
|
+
self.jinx_objects = {} # command_name -> Jinx object
|
|
15
16
|
|
|
16
17
|
def route(self, command: str, help_text: str = "") -> Callable:
|
|
17
18
|
def wrapper(func):
|
|
@@ -42,12 +43,17 @@ class CommandRouter:
|
|
|
42
43
|
|
|
43
44
|
def register_jinx(self, jinx: Jinx):
|
|
44
45
|
command_name = jinx.jinx_name
|
|
45
|
-
|
|
46
|
+
|
|
46
47
|
def jinx_handler(command: str, **kwargs):
|
|
47
48
|
return self._execute_jinx(jinx, command, **kwargs)
|
|
48
|
-
|
|
49
|
+
|
|
49
50
|
self.jinx_routes[command_name] = jinx_handler
|
|
51
|
+
self.jinx_objects[command_name] = jinx
|
|
50
52
|
self.help_info[command_name] = jinx.description or "Jinx command"
|
|
53
|
+
|
|
54
|
+
def is_interactive(self, command_name: str) -> bool:
|
|
55
|
+
jinx = self.jinx_objects.get(command_name)
|
|
56
|
+
return bool(jinx and getattr(jinx, 'interactive', False))
|
|
51
57
|
|
|
52
58
|
def _execute_jinx(self, jinx: Jinx, command: str, **kwargs):
|
|
53
59
|
messages = kwargs.get("messages", [])
|
|
@@ -114,7 +114,7 @@ steps:
|
|
|
114
114
|
|
|
115
115
|
line = str(items[idx])[:width-2]
|
|
116
116
|
if current_tab in [0, 1, 2] and idx == selected:
|
|
117
|
-
sys.stdout.write(f'\033[
|
|
117
|
+
sys.stdout.write(f'\033[7;1m>{line.ljust(width-2)}\033[0m')
|
|
118
118
|
else:
|
|
119
119
|
# Color gold/cliff markers
|
|
120
120
|
if '[GOLD]' in line:
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
jinx_name: config_tui
|
|
2
|
+
description: Interactive TUI editor for npcsh configuration (~/.npcshrc)
|
|
3
|
+
interactive: true
|
|
4
|
+
inputs: []
|
|
5
|
+
steps:
|
|
6
|
+
- name: config_editor
|
|
7
|
+
engine: python
|
|
8
|
+
code: |
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
import tty
|
|
12
|
+
import termios
|
|
13
|
+
import select
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
if not sys.stdin.isatty():
|
|
17
|
+
context['output'] = "Config TUI requires an interactive terminal."
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
# ========== Config Items ==========
|
|
21
|
+
CONFIG_ITEMS = [
|
|
22
|
+
{'key': 'NPCSH_CHAT_MODEL', 'label': 'Chat Model', 'type': 'text', 'shortcut': 'model'},
|
|
23
|
+
{'key': 'NPCSH_CHAT_PROVIDER', 'label': 'Chat Provider', 'type': 'text', 'shortcut': 'provider'},
|
|
24
|
+
{'key': 'NPCSH_VISION_MODEL', 'label': 'Vision Model', 'type': 'text'},
|
|
25
|
+
{'key': 'NPCSH_VISION_PROVIDER', 'label': 'Vision Provider', 'type': 'text'},
|
|
26
|
+
{'key': 'NPCSH_EMBEDDING_MODEL', 'label': 'Embedding Model', 'type': 'text'},
|
|
27
|
+
{'key': 'NPCSH_EMBEDDING_PROVIDER', 'label': 'Embedding Provider', 'type': 'text'},
|
|
28
|
+
{'key': 'NPCSH_REASONING_MODEL', 'label': 'Reasoning Model', 'type': 'text'},
|
|
29
|
+
{'key': 'NPCSH_REASONING_PROVIDER', 'label': 'Reasoning Provider', 'type': 'text'},
|
|
30
|
+
{'key': 'NPCSH_DEFAULT_MODE', 'label': 'Default Mode', 'type': 'choice', 'choices': ['agent', 'chat', 'code']},
|
|
31
|
+
{'key': 'NPCSH_STREAM_OUTPUT', 'label': 'Stream Output', 'type': 'toggle'},
|
|
32
|
+
{'key': 'NPCSH_BUILD_KG', 'label': 'Build Knowledge Graph', 'type': 'toggle'},
|
|
33
|
+
{'key': 'NPCSH_SEARCH_PROVIDER', 'label': 'Search Provider', 'type': 'choice', 'choices': ['duckduckgo', 'google', 'bing']},
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
# ========== State ==========
|
|
37
|
+
class ConfigState:
|
|
38
|
+
def __init__(self):
|
|
39
|
+
self.selected_idx = 0
|
|
40
|
+
self.scroll_offset = 0
|
|
41
|
+
self.editing = False
|
|
42
|
+
self.edit_buffer = ""
|
|
43
|
+
self.edit_cursor = 0
|
|
44
|
+
self.values = {}
|
|
45
|
+
self.modified = set()
|
|
46
|
+
self.status = ""
|
|
47
|
+
|
|
48
|
+
state = ConfigState()
|
|
49
|
+
|
|
50
|
+
# ========== Helpers ==========
|
|
51
|
+
def get_size():
|
|
52
|
+
try:
|
|
53
|
+
s = os.get_terminal_size()
|
|
54
|
+
return s.columns, s.lines
|
|
55
|
+
except:
|
|
56
|
+
return 80, 24
|
|
57
|
+
|
|
58
|
+
def load_values():
|
|
59
|
+
"""Load current values from environment and npcshrc."""
|
|
60
|
+
for item in CONFIG_ITEMS:
|
|
61
|
+
key = item['key']
|
|
62
|
+
# First try environment
|
|
63
|
+
value = os.environ.get(key, '')
|
|
64
|
+
if not value:
|
|
65
|
+
# Try reading from npcshrc
|
|
66
|
+
npcshrc = Path.home() / '.npcshrc'
|
|
67
|
+
if npcshrc.exists():
|
|
68
|
+
with open(npcshrc) as f:
|
|
69
|
+
for line in f:
|
|
70
|
+
if line.strip().startswith(f'export {key}='):
|
|
71
|
+
value = line.split('=', 1)[1].strip().strip('"').strip("'")
|
|
72
|
+
break
|
|
73
|
+
state.values[key] = value
|
|
74
|
+
|
|
75
|
+
def save_values():
|
|
76
|
+
"""Save modified values to ~/.npcshrc."""
|
|
77
|
+
from npcsh.config import set_npcsh_config_value
|
|
78
|
+
for key in state.modified:
|
|
79
|
+
set_npcsh_config_value(key, state.values[key])
|
|
80
|
+
state.modified.clear()
|
|
81
|
+
state.status = "Saved!"
|
|
82
|
+
|
|
83
|
+
def format_value(item, value):
|
|
84
|
+
"""Format value for display."""
|
|
85
|
+
if item['type'] == 'toggle':
|
|
86
|
+
return '\033[32mON\033[0m' if value in ('1', 'true', 'True', True) else '\033[31mOFF\033[0m'
|
|
87
|
+
elif not value:
|
|
88
|
+
return '\033[90m(not set)\033[0m'
|
|
89
|
+
return value
|
|
90
|
+
|
|
91
|
+
# ========== Rendering ==========
|
|
92
|
+
def render_screen():
|
|
93
|
+
width, height = get_size()
|
|
94
|
+
out = []
|
|
95
|
+
out.append("\033[2J\033[H")
|
|
96
|
+
|
|
97
|
+
# Header
|
|
98
|
+
header = " NPCSH Configuration "
|
|
99
|
+
out.append(f"\033[1;1H\033[44;37;1m{'=' * width}\033[0m")
|
|
100
|
+
out.append(f"\033[1;{(width - len(header)) // 2}H\033[44;37;1m{header}\033[0m")
|
|
101
|
+
|
|
102
|
+
# Config items
|
|
103
|
+
visible_height = height - 6
|
|
104
|
+
visible = CONFIG_ITEMS[state.scroll_offset:state.scroll_offset + visible_height]
|
|
105
|
+
|
|
106
|
+
label_width = max(len(item['label']) for item in CONFIG_ITEMS) + 2
|
|
107
|
+
value_width = width - label_width - 10
|
|
108
|
+
|
|
109
|
+
for i, item in enumerate(visible):
|
|
110
|
+
row = 3 + i
|
|
111
|
+
idx = i + state.scroll_offset
|
|
112
|
+
key = item['key']
|
|
113
|
+
value = state.values.get(key, '')
|
|
114
|
+
display_value = format_value(item, value)
|
|
115
|
+
|
|
116
|
+
# Indicator for modified
|
|
117
|
+
mod_indicator = '*' if key in state.modified else ' '
|
|
118
|
+
|
|
119
|
+
if idx == state.selected_idx:
|
|
120
|
+
if state.editing:
|
|
121
|
+
# Show edit mode
|
|
122
|
+
out.append(f"\033[{row};2H\033[7m{item['label']:<{label_width}}\033[0m")
|
|
123
|
+
# Edit buffer with cursor
|
|
124
|
+
cursor_pos = min(state.edit_cursor, len(state.edit_buffer))
|
|
125
|
+
before = state.edit_buffer[:cursor_pos]
|
|
126
|
+
after = state.edit_buffer[cursor_pos:]
|
|
127
|
+
out.append(f"\033[{row};{label_width+4}H{before}\033[7m \033[0m{after}")
|
|
128
|
+
else:
|
|
129
|
+
out.append(f"\033[{row};2H\033[7m{mod_indicator}{item['label']:<{label_width}} {display_value[:value_width]}\033[0m")
|
|
130
|
+
else:
|
|
131
|
+
out.append(f"\033[{row};2H{mod_indicator}{item['label']:<{label_width}} {display_value[:value_width]}")
|
|
132
|
+
|
|
133
|
+
# Type indicator
|
|
134
|
+
type_hint = {'text': '[e]', 'toggle': '[t]', 'choice': '[c]'}.get(item['type'], '')
|
|
135
|
+
out.append(f"\033[{row};{width-4}H\033[90m{type_hint}\033[0m")
|
|
136
|
+
|
|
137
|
+
# Status line
|
|
138
|
+
if state.status:
|
|
139
|
+
out.append(f"\033[{height-2};2H\033[33m{state.status}\033[0m")
|
|
140
|
+
|
|
141
|
+
# Footer
|
|
142
|
+
if state.editing:
|
|
143
|
+
footer = "[Enter] Save [Esc] Cancel"
|
|
144
|
+
else:
|
|
145
|
+
footer = "[j/k] Navigate [e] Edit [t] Toggle [s] Save All [q] Quit"
|
|
146
|
+
out.append(f"\033[{height};1H\033[90m{footer[:width]}\033[0m")
|
|
147
|
+
|
|
148
|
+
sys.stdout.write(''.join(out))
|
|
149
|
+
sys.stdout.flush()
|
|
150
|
+
|
|
151
|
+
# ========== Input Handling ==========
|
|
152
|
+
def handle_input(c):
|
|
153
|
+
if state.editing:
|
|
154
|
+
return handle_edit_input(c)
|
|
155
|
+
|
|
156
|
+
if c == 'q':
|
|
157
|
+
if state.modified:
|
|
158
|
+
state.status = "Unsaved changes! Press 's' to save or 'q' again to discard."
|
|
159
|
+
return True
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
if c == '\x1b': # Escape sequence
|
|
163
|
+
if select.select([sys.stdin], [], [], 0.05)[0]:
|
|
164
|
+
c2 = sys.stdin.read(1)
|
|
165
|
+
if c2 == '[':
|
|
166
|
+
c3 = sys.stdin.read(1)
|
|
167
|
+
if c3 == 'A': # Up
|
|
168
|
+
move_up()
|
|
169
|
+
elif c3 == 'B': # Down
|
|
170
|
+
move_down()
|
|
171
|
+
return True
|
|
172
|
+
|
|
173
|
+
if c == 'k':
|
|
174
|
+
move_up()
|
|
175
|
+
elif c == 'j':
|
|
176
|
+
move_down()
|
|
177
|
+
elif c == 'e' or c == '\r' or c == '\n':
|
|
178
|
+
start_edit()
|
|
179
|
+
elif c == 't':
|
|
180
|
+
toggle_value()
|
|
181
|
+
elif c == 'c':
|
|
182
|
+
cycle_choice()
|
|
183
|
+
elif c == 's':
|
|
184
|
+
save_values()
|
|
185
|
+
|
|
186
|
+
return True
|
|
187
|
+
|
|
188
|
+
def handle_edit_input(c):
|
|
189
|
+
if c == '\x1b': # Escape - cancel edit
|
|
190
|
+
state.editing = False
|
|
191
|
+
state.edit_buffer = ""
|
|
192
|
+
state.status = "Edit cancelled"
|
|
193
|
+
return True
|
|
194
|
+
|
|
195
|
+
if c == '\r' or c == '\n': # Enter - save edit
|
|
196
|
+
key = CONFIG_ITEMS[state.selected_idx]['key']
|
|
197
|
+
state.values[key] = state.edit_buffer
|
|
198
|
+
state.modified.add(key)
|
|
199
|
+
state.editing = False
|
|
200
|
+
state.edit_buffer = ""
|
|
201
|
+
state.status = f"Changed {key}"
|
|
202
|
+
return True
|
|
203
|
+
|
|
204
|
+
if c == '\x7f' or c == '\x08': # Backspace
|
|
205
|
+
if state.edit_cursor > 0:
|
|
206
|
+
state.edit_buffer = state.edit_buffer[:state.edit_cursor-1] + state.edit_buffer[state.edit_cursor:]
|
|
207
|
+
state.edit_cursor -= 1
|
|
208
|
+
return True
|
|
209
|
+
|
|
210
|
+
if c >= ' ' and c <= '~': # Printable
|
|
211
|
+
state.edit_buffer = state.edit_buffer[:state.edit_cursor] + c + state.edit_buffer[state.edit_cursor:]
|
|
212
|
+
state.edit_cursor += 1
|
|
213
|
+
return True
|
|
214
|
+
|
|
215
|
+
return True
|
|
216
|
+
|
|
217
|
+
def move_up():
|
|
218
|
+
state.selected_idx = max(0, state.selected_idx - 1)
|
|
219
|
+
if state.selected_idx < state.scroll_offset:
|
|
220
|
+
state.scroll_offset = state.selected_idx
|
|
221
|
+
state.status = ""
|
|
222
|
+
|
|
223
|
+
def move_down():
|
|
224
|
+
_, height = get_size()
|
|
225
|
+
visible_height = height - 6
|
|
226
|
+
state.selected_idx = min(len(CONFIG_ITEMS) - 1, state.selected_idx + 1)
|
|
227
|
+
if state.selected_idx >= state.scroll_offset + visible_height:
|
|
228
|
+
state.scroll_offset = state.selected_idx - visible_height + 1
|
|
229
|
+
state.status = ""
|
|
230
|
+
|
|
231
|
+
def start_edit():
|
|
232
|
+
item = CONFIG_ITEMS[state.selected_idx]
|
|
233
|
+
if item['type'] == 'toggle':
|
|
234
|
+
toggle_value()
|
|
235
|
+
elif item['type'] == 'choice':
|
|
236
|
+
cycle_choice()
|
|
237
|
+
else:
|
|
238
|
+
key = item['key']
|
|
239
|
+
state.edit_buffer = state.values.get(key, '')
|
|
240
|
+
state.edit_cursor = len(state.edit_buffer)
|
|
241
|
+
state.editing = True
|
|
242
|
+
state.status = "Editing... Enter to save, Esc to cancel"
|
|
243
|
+
|
|
244
|
+
def toggle_value():
|
|
245
|
+
item = CONFIG_ITEMS[state.selected_idx]
|
|
246
|
+
if item['type'] != 'toggle':
|
|
247
|
+
return
|
|
248
|
+
key = item['key']
|
|
249
|
+
current = state.values.get(key, '0')
|
|
250
|
+
new_value = '0' if current in ('1', 'true', 'True') else '1'
|
|
251
|
+
state.values[key] = new_value
|
|
252
|
+
state.modified.add(key)
|
|
253
|
+
state.status = f"Toggled {item['label']}"
|
|
254
|
+
|
|
255
|
+
def cycle_choice():
|
|
256
|
+
item = CONFIG_ITEMS[state.selected_idx]
|
|
257
|
+
if item['type'] != 'choice':
|
|
258
|
+
return
|
|
259
|
+
key = item['key']
|
|
260
|
+
choices = item.get('choices', [])
|
|
261
|
+
if not choices:
|
|
262
|
+
return
|
|
263
|
+
current = state.values.get(key, '')
|
|
264
|
+
try:
|
|
265
|
+
idx = choices.index(current)
|
|
266
|
+
next_idx = (idx + 1) % len(choices)
|
|
267
|
+
except ValueError:
|
|
268
|
+
next_idx = 0
|
|
269
|
+
state.values[key] = choices[next_idx]
|
|
270
|
+
state.modified.add(key)
|
|
271
|
+
state.status = f"Changed to {choices[next_idx]}"
|
|
272
|
+
|
|
273
|
+
# ========== Main Loop ==========
|
|
274
|
+
load_values()
|
|
275
|
+
|
|
276
|
+
fd = sys.stdin.fileno()
|
|
277
|
+
old_settings = termios.tcgetattr(fd)
|
|
278
|
+
|
|
279
|
+
try:
|
|
280
|
+
tty.setcbreak(fd)
|
|
281
|
+
sys.stdout.write('\033[?25l') # Hide cursor
|
|
282
|
+
|
|
283
|
+
render_screen()
|
|
284
|
+
|
|
285
|
+
while True:
|
|
286
|
+
c = sys.stdin.read(1)
|
|
287
|
+
if not handle_input(c):
|
|
288
|
+
break
|
|
289
|
+
render_screen()
|
|
290
|
+
|
|
291
|
+
finally:
|
|
292
|
+
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
|
293
|
+
sys.stdout.write('\033[?25h') # Show cursor
|
|
294
|
+
sys.stdout.write('\033[2J\033[H') # Clear screen
|
|
295
|
+
sys.stdout.flush()
|
|
296
|
+
|
|
297
|
+
if state.modified:
|
|
298
|
+
context['output'] = f"Exited with unsaved changes: {', '.join(state.modified)}"
|
|
299
|
+
else:
|
|
300
|
+
context['output'] = "Configuration editor closed."
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
jinx_name: guac
|
|
2
|
-
description: Interactive Python
|
|
2
|
+
description: Interactive Python TUI - live variable inspector, code execution, DataFrame viewer
|
|
3
3
|
inputs:
|
|
4
4
|
- model: null
|
|
5
5
|
- provider: null
|
|
@@ -24,8 +24,6 @@ steps:
|
|
|
24
24
|
|
|
25
25
|
import numpy as np
|
|
26
26
|
import pandas as pd
|
|
27
|
-
import matplotlib
|
|
28
|
-
matplotlib.use('Agg') # Non-interactive backend for TUI
|
|
29
27
|
import matplotlib.pyplot as plt
|
|
30
28
|
|
|
31
29
|
from npcpy.llm_funcs import get_llm_response
|
|
@@ -204,7 +202,7 @@ steps:
|
|
|
204
202
|
# ===== HEADER =====
|
|
205
203
|
mode_colors = {"code": "\033[32m", "natural": "\033[35m", "inspect": "\033[33m"}
|
|
206
204
|
mode_str = f"{mode_colors[state.mode]}[{state.mode}]\033[0m"
|
|
207
|
-
header = f" GUAC - Python
|
|
205
|
+
header = f" GUAC - Interactive Python {mode_str} "
|
|
208
206
|
status_color = "\033[33m" if "..." in state.status else "\033[32m"
|
|
209
207
|
out.append(f"\033[1;1H\033[42;30;1m{header.ljust(width)}\033[0m")
|
|
210
208
|
out.append(f"\033[1;{width-len(state.status)-3}H{status_color}[{state.status}]\033[0m")
|
|
@@ -277,7 +275,7 @@ steps:
|
|
|
277
275
|
tabs = ""
|
|
278
276
|
for i, name in enumerate(panel_names):
|
|
279
277
|
if i == state.panel:
|
|
280
|
-
tabs += f"\033[
|
|
278
|
+
tabs += f"\033[7m {name} \033[0m"
|
|
281
279
|
else:
|
|
282
280
|
tabs += f"\033[90m {name} \033[0m"
|
|
283
281
|
out.append(f"\033[3;{right_x}H{tabs}")
|
|
@@ -293,7 +291,7 @@ steps:
|
|
|
293
291
|
info = var_info(name, value)
|
|
294
292
|
display = f"{name[:10]:<10} {info[:rpanel_w-12]}"
|
|
295
293
|
if idx == state.selected_var:
|
|
296
|
-
out.append(f"\033[{4+i};{right_x}H\033[
|
|
294
|
+
out.append(f"\033[{4+i};{right_x}H\033[7m>{display[:rpanel_w]}\033[0m")
|
|
297
295
|
elif isinstance(value, pd.DataFrame):
|
|
298
296
|
out.append(f"\033[{4+i};{right_x}H\033[34m {display[:rpanel_w]}\033[0m")
|
|
299
297
|
elif isinstance(value, np.ndarray):
|