npcsh 1.1.16__py3-none-any.whl → 1.1.18__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 +138 -100
- npcsh/alicanto.py +2 -2
- npcsh/benchmark/__init__.py +28 -0
- npcsh/benchmark/npcsh_agent.py +296 -0
- npcsh/benchmark/runner.py +611 -0
- npcsh/benchmark/templates/install-npcsh.sh.j2 +35 -0
- npcsh/build.py +2 -4
- npcsh/completion.py +2 -6
- npcsh/config.py +1 -3
- npcsh/conversation_viewer.py +389 -0
- npcsh/corca.py +0 -1
- npcsh/execution.py +0 -1
- npcsh/guac.py +0 -1
- npcsh/mcp_helpers.py +2 -3
- npcsh/mcp_server.py +5 -10
- npcsh/npc.py +10 -11
- npcsh/npc_team/jinxs/bin/benchmark.jinx +146 -0
- npcsh/npc_team/jinxs/bin/nql.jinx +7 -7
- npcsh/npc_team/jinxs/bin/roll.jinx +20 -23
- npcsh/npc_team/jinxs/bin/sample.jinx +6 -7
- npcsh/npc_team/jinxs/bin/sync.jinx +6 -6
- npcsh/npc_team/jinxs/bin/vixynt.jinx +8 -8
- npcsh/npc_team/jinxs/incognide/add_tab.jinx +11 -0
- npcsh/npc_team/jinxs/incognide/close_pane.jinx +9 -0
- npcsh/npc_team/jinxs/incognide/close_tab.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/confirm.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/focus_pane.jinx +9 -0
- npcsh/npc_team/jinxs/{npc_studio/npc-studio.jinx → incognide/incognide.jinx} +2 -2
- npcsh/npc_team/jinxs/incognide/list_panes.jinx +8 -0
- npcsh/npc_team/jinxs/incognide/navigate.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/notify.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/open_pane.jinx +13 -0
- npcsh/npc_team/jinxs/incognide/read_pane.jinx +9 -0
- npcsh/npc_team/jinxs/incognide/run_terminal.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/send_message.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/split_pane.jinx +12 -0
- npcsh/npc_team/jinxs/incognide/switch_npc.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/switch_tab.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/write_file.jinx +11 -0
- npcsh/npc_team/jinxs/incognide/zen_mode.jinx +9 -0
- npcsh/npc_team/jinxs/lib/browser/browser_action.jinx +4 -4
- npcsh/npc_team/jinxs/lib/browser/browser_screenshot.jinx +1 -1
- npcsh/npc_team/jinxs/lib/browser/open_browser.jinx +2 -2
- npcsh/npc_team/jinxs/lib/computer_use/click.jinx +2 -2
- npcsh/npc_team/jinxs/lib/computer_use/key_press.jinx +1 -1
- npcsh/npc_team/jinxs/lib/computer_use/launch_app.jinx +1 -1
- npcsh/npc_team/jinxs/lib/computer_use/screenshot.jinx +1 -1
- npcsh/npc_team/jinxs/lib/computer_use/trigger.jinx +2 -2
- npcsh/npc_team/jinxs/lib/computer_use/type_text.jinx +1 -1
- npcsh/npc_team/jinxs/lib/computer_use/wait.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/chat.jinx +4 -4
- npcsh/npc_team/jinxs/lib/core/cmd.jinx +4 -4
- npcsh/npc_team/jinxs/lib/core/compress.jinx +8 -8
- npcsh/npc_team/jinxs/lib/core/edit_file.jinx +3 -0
- npcsh/npc_team/jinxs/lib/core/ots.jinx +7 -7
- npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +348 -0
- npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +339 -0
- npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +418 -0
- npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +73 -0
- npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +388 -0
- npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +283 -0
- npcsh/npc_team/jinxs/lib/core/search.jinx +52 -129
- npcsh/npc_team/jinxs/lib/core/sh.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/sleep.jinx +29 -18
- npcsh/npc_team/jinxs/lib/core/sql.jinx +15 -11
- npcsh/npc_team/jinxs/lib/orchestration/convene.jinx +7 -7
- npcsh/npc_team/jinxs/lib/orchestration/delegate.jinx +8 -9
- npcsh/npc_team/jinxs/lib/research/paper_search.jinx +389 -78
- npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +373 -56
- npcsh/npc_team/jinxs/lib/utils/build.jinx +5 -5
- npcsh/npc_team/jinxs/lib/utils/compile.jinx +2 -2
- npcsh/npc_team/jinxs/lib/utils/help.jinx +1 -1
- npcsh/npc_team/jinxs/lib/utils/init.jinx +5 -5
- npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +300 -145
- npcsh/npc_team/jinxs/lib/utils/serve.jinx +2 -2
- npcsh/npc_team/jinxs/lib/utils/set.jinx +2 -2
- npcsh/npc_team/jinxs/lib/utils/switch.jinx +3 -3
- npcsh/npc_team/jinxs/lib/utils/switches.jinx +1 -1
- npcsh/npc_team/jinxs/lib/utils/teamviz.jinx +2 -2
- npcsh/npc_team/jinxs/modes/alicanto.jinx +356 -0
- npcsh/npc_team/jinxs/modes/arxiv.jinx +720 -0
- npcsh/npc_team/jinxs/modes/corca.jinx +430 -0
- npcsh/npc_team/jinxs/modes/guac.jinx +544 -0
- npcsh/npc_team/jinxs/modes/plonk.jinx +379 -0
- npcsh/npc_team/jinxs/modes/pti.jinx +357 -0
- npcsh/npc_team/jinxs/modes/reattach.jinx +291 -0
- npcsh/npc_team/jinxs/modes/spool.jinx +350 -0
- npcsh/npc_team/jinxs/modes/wander.jinx +455 -0
- {npcsh-1.1.16.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/modes}/yap.jinx +8 -2
- npcsh/npc_team/sibiji.npc +1 -1
- npcsh/npcsh.py +87 -46
- npcsh/plonk.py +0 -1
- npcsh/pti.py +0 -1
- npcsh/routes.py +1 -3
- npcsh/spool.py +0 -1
- npcsh/ui.py +0 -1
- npcsh/wander.py +0 -1
- npcsh/yap.py +0 -1
- npcsh-1.1.18.data/data/npcsh/npc_team/add_tab.jinx +11 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/alicanto.jinx +356 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/arxiv.jinx +720 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/benchmark.jinx +146 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/browser_action.jinx +4 -4
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/browser_screenshot.jinx +1 -1
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/build.jinx +5 -5
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/chat.jinx +4 -4
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/click.jinx +2 -2
- npcsh-1.1.18.data/data/npcsh/npc_team/close_pane.jinx +9 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/close_tab.jinx +10 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/cmd.jinx +4 -4
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/compile.jinx +2 -2
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/compress.jinx +8 -8
- npcsh-1.1.18.data/data/npcsh/npc_team/confirm.jinx +10 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/convene.jinx +7 -7
- npcsh-1.1.18.data/data/npcsh/npc_team/corca.jinx +430 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/db_search.jinx +348 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/delegate.jinx +8 -9
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/edit_file.jinx +3 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/file_search.jinx +339 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/focus_pane.jinx +9 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/guac.jinx +544 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/help.jinx +1 -1
- npcsh-1.1.16.data/data/npcsh/npc_team/npc-studio.jinx → npcsh-1.1.18.data/data/npcsh/npc_team/incognide.jinx +2 -2
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/init.jinx +5 -5
- npcsh-1.1.18.data/data/npcsh/npc_team/jinxs.jinx +331 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/key_press.jinx +1 -1
- npcsh-1.1.18.data/data/npcsh/npc_team/kg_search.jinx +418 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/launch_app.jinx +1 -1
- npcsh-1.1.18.data/data/npcsh/npc_team/list_panes.jinx +8 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/mem_review.jinx +73 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/mem_search.jinx +388 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/navigate.jinx +10 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/notify.jinx +10 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/nql.jinx +7 -7
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/open_browser.jinx +2 -2
- npcsh-1.1.18.data/data/npcsh/npc_team/open_pane.jinx +13 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/ots.jinx +7 -7
- npcsh-1.1.18.data/data/npcsh/npc_team/paper_search.jinx +412 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/plonk.jinx +379 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/pti.jinx +357 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/read_pane.jinx +9 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/reattach.jinx +291 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/roll.jinx +20 -23
- npcsh-1.1.18.data/data/npcsh/npc_team/run_terminal.jinx +10 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sample.jinx +6 -7
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/screenshot.jinx +1 -1
- npcsh-1.1.18.data/data/npcsh/npc_team/search.jinx +54 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/semantic_scholar.jinx +386 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/send_message.jinx +10 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/serve.jinx +2 -2
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/set.jinx +2 -2
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sh.jinx +1 -1
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sibiji.npc +1 -1
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sleep.jinx +29 -18
- npcsh-1.1.18.data/data/npcsh/npc_team/split_pane.jinx +12 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/spool.jinx +350 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/sql.jinx +20 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/switch.jinx +3 -3
- npcsh-1.1.18.data/data/npcsh/npc_team/switch_npc.jinx +10 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/switch_tab.jinx +10 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/switches.jinx +1 -1
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sync.jinx +6 -6
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/teamviz.jinx +2 -2
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/trigger.jinx +2 -2
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/type_text.jinx +1 -1
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/vixynt.jinx +8 -8
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/wait.jinx +1 -1
- npcsh-1.1.18.data/data/npcsh/npc_team/wander.jinx +455 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/web_search.jinx +283 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/write_file.jinx +11 -0
- {npcsh/npc_team/jinxs/bin → npcsh-1.1.18.data/data/npcsh/npc_team}/yap.jinx +8 -2
- npcsh-1.1.18.data/data/npcsh/npc_team/zen_mode.jinx +9 -0
- {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/METADATA +99 -7
- npcsh-1.1.18.dist-info/RECORD +235 -0
- {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/WHEEL +1 -1
- {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/entry_points.txt +2 -3
- npcsh/npc_team/jinxs/bin/spool.jinx +0 -161
- npcsh/npc_team/jinxs/bin/wander.jinx +0 -152
- npcsh/npc_team/jinxs/lib/research/arxiv.jinx +0 -76
- npcsh-1.1.16.data/data/npcsh/npc_team/arxiv.jinx +0 -76
- npcsh-1.1.16.data/data/npcsh/npc_team/jinxs.jinx +0 -176
- npcsh-1.1.16.data/data/npcsh/npc_team/paper_search.jinx +0 -101
- npcsh-1.1.16.data/data/npcsh/npc_team/search.jinx +0 -131
- npcsh-1.1.16.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -69
- npcsh-1.1.16.data/data/npcsh/npc_team/spool.jinx +0 -161
- npcsh-1.1.16.data/data/npcsh/npc_team/sql.jinx +0 -16
- npcsh-1.1.16.data/data/npcsh/npc_team/wander.jinx +0 -152
- npcsh-1.1.16.dist-info/RECORD +0 -170
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/alicanto.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/corca.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/corca_example.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/frederic.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/guac.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/load_file.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/paste.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonk.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonkjr.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/python.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/shh.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/usage.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/verbose.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/licenses/LICENSE +0 -0
- {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
jinx_name: web_search
|
|
2
|
+
description: Search the web with interactive TUI
|
|
3
|
+
inputs:
|
|
4
|
+
- query: ""
|
|
5
|
+
- provider: ""
|
|
6
|
+
- num_results: "10"
|
|
7
|
+
- text: "false"
|
|
8
|
+
|
|
9
|
+
steps:
|
|
10
|
+
- name: search_web
|
|
11
|
+
engine: python
|
|
12
|
+
code: |
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import tty
|
|
16
|
+
import termios
|
|
17
|
+
import webbrowser
|
|
18
|
+
import subprocess
|
|
19
|
+
from npcpy.data.web import search_web
|
|
20
|
+
|
|
21
|
+
query = context.get('query', '').strip()
|
|
22
|
+
text_mode = context.get('text', '').lower() in ('true', '1', 'yes')
|
|
23
|
+
|
|
24
|
+
if not query:
|
|
25
|
+
lines = [
|
|
26
|
+
"Usage: /web_search <query>",
|
|
27
|
+
"",
|
|
28
|
+
"Options:",
|
|
29
|
+
" provider - Search provider (default uses state.search_provider)",
|
|
30
|
+
" num_results - Number of results (default 10)",
|
|
31
|
+
" text - Text-only output, no TUI (true/false)",
|
|
32
|
+
"",
|
|
33
|
+
"TUI Controls:",
|
|
34
|
+
" j/k or arrows - Navigate",
|
|
35
|
+
" p - Preview page content",
|
|
36
|
+
" o - Open in browser",
|
|
37
|
+
" i - Open in incognide",
|
|
38
|
+
" c - Copy URL to clipboard",
|
|
39
|
+
" q/ESC - Quit",
|
|
40
|
+
"",
|
|
41
|
+
"Examples:",
|
|
42
|
+
" /web_search python asyncio tutorial",
|
|
43
|
+
" /web_search react hooks num_results=20",
|
|
44
|
+
]
|
|
45
|
+
context['output'] = "\n".join(lines)
|
|
46
|
+
else:
|
|
47
|
+
provider = context.get('provider') or None
|
|
48
|
+
try:
|
|
49
|
+
provider = provider or (state.search_provider if 'state' in dir() and state else None)
|
|
50
|
+
except:
|
|
51
|
+
pass
|
|
52
|
+
num_results = int(context.get('num_results') or 10)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
raw_results = search_web(query, provider=provider, num_results=num_results)
|
|
56
|
+
|
|
57
|
+
# Normalize results
|
|
58
|
+
results = []
|
|
59
|
+
if raw_results:
|
|
60
|
+
for res in raw_results:
|
|
61
|
+
if isinstance(res, dict):
|
|
62
|
+
results.append({
|
|
63
|
+
'title': res.get('title', 'No title'),
|
|
64
|
+
'url': res.get('url', res.get('link', '')),
|
|
65
|
+
'snippet': res.get('snippet', res.get('description', ''))
|
|
66
|
+
})
|
|
67
|
+
else:
|
|
68
|
+
results.append({'title': str(res)[:50], 'url': str(res), 'snippet': ''})
|
|
69
|
+
|
|
70
|
+
if not results:
|
|
71
|
+
context['output'] = "No web results found."
|
|
72
|
+
elif text_mode:
|
|
73
|
+
# Text-only output
|
|
74
|
+
lines = []
|
|
75
|
+
for res in results:
|
|
76
|
+
lines.append(f"- {res['title']}")
|
|
77
|
+
lines.append(f" {res['url']}")
|
|
78
|
+
if res.get('snippet'):
|
|
79
|
+
lines.append(f" {res['snippet'][:100]}...")
|
|
80
|
+
lines.append("")
|
|
81
|
+
context['output'] = "\n".join(lines)
|
|
82
|
+
else:
|
|
83
|
+
# Interactive TUI mode
|
|
84
|
+
def get_terminal_size():
|
|
85
|
+
try:
|
|
86
|
+
size = os.get_terminal_size()
|
|
87
|
+
return size.columns, size.lines
|
|
88
|
+
except:
|
|
89
|
+
return 80, 24
|
|
90
|
+
|
|
91
|
+
def get_domain(url):
|
|
92
|
+
try:
|
|
93
|
+
from urllib.parse import urlparse
|
|
94
|
+
return urlparse(url).netloc[:25]
|
|
95
|
+
except:
|
|
96
|
+
return url[:25]
|
|
97
|
+
|
|
98
|
+
width, height = get_terminal_size()
|
|
99
|
+
selected = 0
|
|
100
|
+
scroll = 0
|
|
101
|
+
list_height = height - 5
|
|
102
|
+
mode = 'list'
|
|
103
|
+
preview_scroll = 0
|
|
104
|
+
preview_content = []
|
|
105
|
+
|
|
106
|
+
fd = sys.stdin.fileno()
|
|
107
|
+
old_settings = termios.tcgetattr(fd)
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
tty.setcbreak(fd)
|
|
111
|
+
sys.stdout.write('\033[?25l')
|
|
112
|
+
sys.stdout.write('\033[2J\033[H')
|
|
113
|
+
|
|
114
|
+
while True:
|
|
115
|
+
width, height = get_terminal_size()
|
|
116
|
+
list_height = height - 5
|
|
117
|
+
|
|
118
|
+
if mode == 'list':
|
|
119
|
+
if selected < scroll:
|
|
120
|
+
scroll = selected
|
|
121
|
+
elif selected >= scroll + list_height:
|
|
122
|
+
scroll = selected - list_height + 1
|
|
123
|
+
|
|
124
|
+
sys.stdout.write('\033[H')
|
|
125
|
+
|
|
126
|
+
# Header
|
|
127
|
+
if mode == 'list':
|
|
128
|
+
header = f" WEB SEARCH ({len(results)} results): '{query}' "
|
|
129
|
+
else:
|
|
130
|
+
header = f" PREVIEW: {results[selected]['title'][:width-12]} "
|
|
131
|
+
sys.stdout.write(f'\033[44;37;1m{header.ljust(width)}\033[0m\n')
|
|
132
|
+
|
|
133
|
+
# Column headers
|
|
134
|
+
if mode == 'list':
|
|
135
|
+
col_header = f' {"#":<3} {"DOMAIN":<25} {"TITLE":<50}'
|
|
136
|
+
sys.stdout.write(f'\033[90m{col_header[:width]}\033[0m\n')
|
|
137
|
+
else:
|
|
138
|
+
sys.stdout.write(f'\033[90m{"─" * width}\033[0m\n')
|
|
139
|
+
|
|
140
|
+
if mode == 'list':
|
|
141
|
+
for i in range(list_height):
|
|
142
|
+
idx = scroll + i
|
|
143
|
+
sys.stdout.write(f'\033[{3+i};1H\033[K')
|
|
144
|
+
if idx >= len(results):
|
|
145
|
+
continue
|
|
146
|
+
|
|
147
|
+
r = results[idx]
|
|
148
|
+
num = str(idx + 1)
|
|
149
|
+
domain = get_domain(r['url'])
|
|
150
|
+
title = r['title'][:55].replace('\n', ' ')
|
|
151
|
+
|
|
152
|
+
line = f" {num:<3} {domain:<25} {title}"
|
|
153
|
+
line = line[:width-1]
|
|
154
|
+
|
|
155
|
+
if idx == selected:
|
|
156
|
+
sys.stdout.write(f'\033[47;30;1m>{line}\033[0m')
|
|
157
|
+
else:
|
|
158
|
+
sys.stdout.write(f' {line}')
|
|
159
|
+
|
|
160
|
+
# Status bar
|
|
161
|
+
sys.stdout.write(f'\033[{height-2};1H\033[K\033[90m{"─" * width}\033[0m')
|
|
162
|
+
sel = results[selected] if results else {}
|
|
163
|
+
snippet = (sel.get('snippet') or '')[:width-2]
|
|
164
|
+
sys.stdout.write(f'\033[{height-1};1H\033[K {snippet}'.ljust(width))
|
|
165
|
+
sys.stdout.write(f'\033[{height};1H\033[K\033[44;37m j/k:Nav p:Preview o:Open i:Incog c:Copy q:Quit [{selected+1}/{len(results)}] \033[0m')
|
|
166
|
+
|
|
167
|
+
else: # preview mode
|
|
168
|
+
for i in range(list_height):
|
|
169
|
+
idx = preview_scroll + i
|
|
170
|
+
sys.stdout.write(f'\033[{3+i};1H\033[K')
|
|
171
|
+
if idx < len(preview_content):
|
|
172
|
+
sys.stdout.write(preview_content[idx][:width-1])
|
|
173
|
+
|
|
174
|
+
sys.stdout.write(f'\033[{height-2};1H\033[K\033[90m{"─" * width}\033[0m')
|
|
175
|
+
sys.stdout.write(f'\033[{height-1};1H\033[K [{preview_scroll+1}/{len(preview_content)} lines]')
|
|
176
|
+
sys.stdout.write(f'\033[{height};1H\033[K\033[44;37m j/k:Scroll b:Back o:Open c:Copy q:Quit \033[0m')
|
|
177
|
+
|
|
178
|
+
sys.stdout.flush()
|
|
179
|
+
|
|
180
|
+
c = sys.stdin.read(1)
|
|
181
|
+
|
|
182
|
+
if c == '\x1b':
|
|
183
|
+
c2 = sys.stdin.read(1)
|
|
184
|
+
if c2 == '[':
|
|
185
|
+
c3 = sys.stdin.read(1)
|
|
186
|
+
if c3 == 'A':
|
|
187
|
+
if mode == 'list' and selected > 0:
|
|
188
|
+
selected -= 1
|
|
189
|
+
elif mode == 'preview' and preview_scroll > 0:
|
|
190
|
+
preview_scroll -= 1
|
|
191
|
+
elif c3 == 'B':
|
|
192
|
+
if mode == 'list' and selected < len(results) - 1:
|
|
193
|
+
selected += 1
|
|
194
|
+
elif mode == 'preview' and preview_scroll < max(0, len(preview_content) - list_height):
|
|
195
|
+
preview_scroll += 1
|
|
196
|
+
else:
|
|
197
|
+
if mode == 'preview':
|
|
198
|
+
mode = 'list'
|
|
199
|
+
sys.stdout.write('\033[2J\033[H')
|
|
200
|
+
else:
|
|
201
|
+
context['output'] = "Cancelled."
|
|
202
|
+
break
|
|
203
|
+
continue
|
|
204
|
+
|
|
205
|
+
if c == 'q' or c == '\x03':
|
|
206
|
+
context['output'] = "Cancelled."
|
|
207
|
+
break
|
|
208
|
+
elif c == 'k':
|
|
209
|
+
if mode == 'list' and selected > 0:
|
|
210
|
+
selected -= 1
|
|
211
|
+
elif mode == 'preview' and preview_scroll > 0:
|
|
212
|
+
preview_scroll -= 1
|
|
213
|
+
elif c == 'j':
|
|
214
|
+
if mode == 'list' and selected < len(results) - 1:
|
|
215
|
+
selected += 1
|
|
216
|
+
elif mode == 'preview' and preview_scroll < max(0, len(preview_content) - list_height):
|
|
217
|
+
preview_scroll += 1
|
|
218
|
+
elif c == 'p' and mode == 'list' and results:
|
|
219
|
+
# Build preview from available info
|
|
220
|
+
sel = results[selected]
|
|
221
|
+
preview_content = [
|
|
222
|
+
f"Title: {sel['title']}",
|
|
223
|
+
"",
|
|
224
|
+
f"URL: {sel['url']}",
|
|
225
|
+
f"Domain: {get_domain(sel['url'])}",
|
|
226
|
+
"",
|
|
227
|
+
]
|
|
228
|
+
if sel.get('snippet'):
|
|
229
|
+
preview_content.append("Snippet:")
|
|
230
|
+
# Word wrap snippet
|
|
231
|
+
words = sel['snippet'].split()
|
|
232
|
+
line = ""
|
|
233
|
+
for w in words:
|
|
234
|
+
if len(line) + len(w) + 1 > width - 4:
|
|
235
|
+
preview_content.append(f" {line}")
|
|
236
|
+
line = w
|
|
237
|
+
else:
|
|
238
|
+
line = f"{line} {w}" if line else w
|
|
239
|
+
if line:
|
|
240
|
+
preview_content.append(f" {line}")
|
|
241
|
+
|
|
242
|
+
mode = 'preview'
|
|
243
|
+
preview_scroll = 0
|
|
244
|
+
sys.stdout.write('\033[2J\033[H')
|
|
245
|
+
elif c == 'b' and mode == 'preview':
|
|
246
|
+
mode = 'list'
|
|
247
|
+
sys.stdout.write('\033[2J\033[H')
|
|
248
|
+
elif c == 'o' and results:
|
|
249
|
+
url = results[selected].get('url', '')
|
|
250
|
+
if url:
|
|
251
|
+
webbrowser.open(url)
|
|
252
|
+
elif c == 'i' and results:
|
|
253
|
+
url = results[selected].get('url', '')
|
|
254
|
+
if url:
|
|
255
|
+
try:
|
|
256
|
+
subprocess.run(['npcsh', '-c', f'/navigate url={url}'], check=False, capture_output=True)
|
|
257
|
+
except:
|
|
258
|
+
webbrowser.open(url)
|
|
259
|
+
elif c == 'c' and results:
|
|
260
|
+
url = results[selected].get('url', '')
|
|
261
|
+
if url:
|
|
262
|
+
try:
|
|
263
|
+
# Try xclip or xsel
|
|
264
|
+
subprocess.run(['xclip', '-selection', 'clipboard'], input=url.encode(), check=True)
|
|
265
|
+
except:
|
|
266
|
+
try:
|
|
267
|
+
subprocess.run(['xsel', '--clipboard', '--input'], input=url.encode(), check=True)
|
|
268
|
+
except:
|
|
269
|
+
pass
|
|
270
|
+
elif c in ('\r', '\n') and results:
|
|
271
|
+
sel = results[selected]
|
|
272
|
+
context['output'] = f"Selected: {sel['title']}\nURL: {sel['url']}"
|
|
273
|
+
break
|
|
274
|
+
|
|
275
|
+
finally:
|
|
276
|
+
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
|
277
|
+
sys.stdout.write('\033[?25h')
|
|
278
|
+
sys.stdout.write('\033[2J\033[H')
|
|
279
|
+
sys.stdout.flush()
|
|
280
|
+
|
|
281
|
+
except Exception as e:
|
|
282
|
+
import traceback
|
|
283
|
+
context['output'] = "Web search error: " + str(e) + "\n" + traceback.format_exc()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
jinx_name: studio.write_file
|
|
2
|
+
description: Write content to an editor pane. Updates the file content in the pane.
|
|
3
|
+
inputs:
|
|
4
|
+
- paneId: "active"
|
|
5
|
+
- content: ""
|
|
6
|
+
- path: ""
|
|
7
|
+
steps:
|
|
8
|
+
- name: frontend_action
|
|
9
|
+
engine: python
|
|
10
|
+
code: |
|
|
11
|
+
context['output'] = "Action executed by frontend"
|
|
@@ -49,8 +49,14 @@ steps:
|
|
|
49
49
|
tts_model = context.get('tts_model', 'kokoro')
|
|
50
50
|
voice = context.get('voice', 'af_heart')
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
# Resolve npc if it's a string (npc name) rather than NPC object
|
|
53
|
+
if isinstance(npc, str) and team:
|
|
54
|
+
npc = team.get(npc) if hasattr(team, 'get') else None
|
|
55
|
+
elif isinstance(npc, str):
|
|
56
|
+
npc = None
|
|
57
|
+
|
|
58
|
+
model = context.get('model') or (npc.model if npc and hasattr(npc, 'model') else None)
|
|
59
|
+
provider = context.get('provider') or (npc.provider if npc and hasattr(npc, 'provider') else None)
|
|
54
60
|
|
|
55
61
|
print("""
|
|
56
62
|
██╗ ██╗ █████╗ ██████╗
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: npcsh
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.18
|
|
4
4
|
Summary: npcsh is a command-line toolkit for using AI agents in novel ways.
|
|
5
5
|
Home-page: https://github.com/NPC-Worldwide/npcsh
|
|
6
6
|
Author: Christopher Agostino
|
|
@@ -66,6 +66,9 @@ Requires-Dist: playsound==1.2.2; extra == "yap"
|
|
|
66
66
|
Requires-Dist: pygame; extra == "yap"
|
|
67
67
|
Requires-Dist: faster_whisper; extra == "yap"
|
|
68
68
|
Requires-Dist: pyttsx3; extra == "yap"
|
|
69
|
+
Provides-Extra: bench
|
|
70
|
+
Requires-Dist: harbor; extra == "bench"
|
|
71
|
+
Requires-Dist: terminal-bench; extra == "bench"
|
|
69
72
|
Provides-Extra: all
|
|
70
73
|
Requires-Dist: anthropic; extra == "all"
|
|
71
74
|
Requires-Dist: openai; extra == "all"
|
|
@@ -593,6 +596,9 @@ npc vixynt "a sunset over mountains"
|
|
|
593
596
|
| `/teamviz` | Visualize team structure. Usage: `/teamviz save=output.png` |
|
|
594
597
|
| `/ots` | Screenshot analysis. Usage: `/ots` then select area |
|
|
595
598
|
| `/sleep` | Evolve knowledge graph. Usage: `/sleep --ops link_facts,deepen` |
|
|
599
|
+
| `/kg_search` | Search knowledge graph with multiple modes. Usage: `/kg_search query mode=hybrid depth=2` |
|
|
600
|
+
| `/mem_search` | Search approved memories. Usage: `/mem_search query status=approved top_k=10` |
|
|
601
|
+
| `/mem_review` | Review pending memories interactively. Usage: `/mem_review limit=50` |
|
|
596
602
|
|
|
597
603
|
### System & Config
|
|
598
604
|
| Command | Description |
|
|
@@ -604,7 +610,7 @@ npc vixynt "a sunset over mountains"
|
|
|
604
610
|
| `/set` | Set config values. Usage: `/set model gemma3:4b`, `/set provider ollama` |
|
|
605
611
|
| `/help` | Show help. Usage: `/help` |
|
|
606
612
|
| `/jinxs` | List available jinxs. Usage: `/jinxs` |
|
|
607
|
-
| `/
|
|
613
|
+
| `/incognide` | Launch Incognide GUI. Usage: `/incognide` |
|
|
608
614
|
| `/trigger` | Set up system triggers. Usage: `/trigger 'description' -m gemma3:27b` |
|
|
609
615
|
|
|
610
616
|
## Common Command-Line Flags:
|
|
@@ -623,6 +629,92 @@ npc vixynt "a sunset over mountains"
|
|
|
623
629
|
--format (-f) | --num_frames (-num_f) | --sprovider (-s) |
|
|
624
630
|
```
|
|
625
631
|
|
|
632
|
+
## Memory & Knowledge Graph
|
|
633
|
+
|
|
634
|
+
`npcsh` maintains a memory lifecycle system that allows agents to learn and grow from past interactions. Memories progress through stages and can be incorporated into a knowledge graph for advanced retrieval.
|
|
635
|
+
|
|
636
|
+
### Memory Lifecycle
|
|
637
|
+
|
|
638
|
+
Memories are extracted from conversations and follow this lifecycle:
|
|
639
|
+
|
|
640
|
+
1. **pending_approval** - New memories awaiting review
|
|
641
|
+
2. **human-approved** - Approved and ready for KG integration
|
|
642
|
+
3. **human-rejected** - Rejected (used as negative examples)
|
|
643
|
+
4. **human-edited** - Modified by user before approval
|
|
644
|
+
5. **skipped** - Deferred for later review
|
|
645
|
+
|
|
646
|
+
### Memory Commands
|
|
647
|
+
|
|
648
|
+
```bash
|
|
649
|
+
# Search through approved memories
|
|
650
|
+
/mem_search python # Keyword search
|
|
651
|
+
/mem_search python status=approved # Filter by status
|
|
652
|
+
/mem_search python top_k=20 # Limit results
|
|
653
|
+
|
|
654
|
+
# Review pending memories interactively
|
|
655
|
+
/mem_review # Review with default limit
|
|
656
|
+
/mem_review limit=50 # Review more at once
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
### Knowledge Graph
|
|
660
|
+
|
|
661
|
+
The knowledge graph stores facts and concepts extracted from approved memories, enabling semantic search and reasoning. Facts are linked to concepts, allowing traversal-based discovery.
|
|
662
|
+
|
|
663
|
+
```bash
|
|
664
|
+
# Keyword search
|
|
665
|
+
/kg_search python # Simple keyword match
|
|
666
|
+
|
|
667
|
+
# Semantic similarity search
|
|
668
|
+
/kg_search python mode=embedding # Find semantically similar facts
|
|
669
|
+
|
|
670
|
+
# Graph traversal search
|
|
671
|
+
/kg_search python mode=link depth=3 # Traverse graph links
|
|
672
|
+
|
|
673
|
+
# Hybrid search (combines methods)
|
|
674
|
+
/kg_search python mode=all # All methods combined
|
|
675
|
+
|
|
676
|
+
# Explore concepts
|
|
677
|
+
/kg_search type=concepts # List all concepts
|
|
678
|
+
/kg_search concept="Machine Learning" # Explore a specific concept
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### Knowledge Graph Evolution
|
|
682
|
+
|
|
683
|
+
The `/sleep` command evolves the knowledge graph through consolidation, abstraction, and creative synthesis:
|
|
684
|
+
|
|
685
|
+
```bash
|
|
686
|
+
# Basic sleep (consolidation)
|
|
687
|
+
/sleep
|
|
688
|
+
|
|
689
|
+
# Import approved memories first, then evolve
|
|
690
|
+
/sleep backfill=true
|
|
691
|
+
|
|
692
|
+
# Dream mode - creative synthesis across domains
|
|
693
|
+
/sleep dream=true
|
|
694
|
+
|
|
695
|
+
# Combined backfill and dream
|
|
696
|
+
/sleep backfill=true dream=true
|
|
697
|
+
|
|
698
|
+
# Specific operations
|
|
699
|
+
/sleep ops=prune,deepen,abstract
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
**Operations:**
|
|
703
|
+
- **prune** - Remove redundant or low-value facts
|
|
704
|
+
- **deepen** - Add detail to existing facts
|
|
705
|
+
- **abstract** - Create higher-level generalizations
|
|
706
|
+
- **link** - Connect related facts and concepts
|
|
707
|
+
|
|
708
|
+
### Environment Variables
|
|
709
|
+
|
|
710
|
+
```bash
|
|
711
|
+
# Enable/disable automatic KG building (default: enabled)
|
|
712
|
+
export NPCSH_BUILD_KG=1
|
|
713
|
+
|
|
714
|
+
# Database path
|
|
715
|
+
export NPCSH_DB_PATH=~/npcsh_history.db
|
|
716
|
+
```
|
|
717
|
+
|
|
626
718
|
## Read the Docs
|
|
627
719
|
To see more about how to use the jinxs and modes in the NPC Shell, read the docs at [npc-shell.readthedocs.io](https://npc-shell.readthedocs.io/en/latest/)
|
|
628
720
|
|
|
@@ -630,16 +722,16 @@ To see more about how to use the jinxs and modes in the NPC Shell, read the docs
|
|
|
630
722
|
## Inference Capabilities
|
|
631
723
|
- `npcsh` works with local and enterprise LLM providers through its LiteLLM integration, allowing users to run inference from Ollama, LMStudio, vLLM, MLX, OpenAI, Anthropic, Gemini, and Deepseek, making it a versatile tool for both simple commands and sophisticated AI-driven tasks.
|
|
632
724
|
|
|
633
|
-
##
|
|
634
|
-
|
|
725
|
+
## Incognide
|
|
726
|
+
Incognide is a desktop workspace environment for integrating LLMs into your workflows in an organized and seamless manner. See the source code for Incognide [here](https://github.com/npc-worldwide/incognide). Download the executables at [our website](https://enpisi.com/downloads). For the most up to date development version, you can use Incognide by invoking it in npcsh
|
|
635
727
|
|
|
636
728
|
```
|
|
637
|
-
/
|
|
729
|
+
/incognide
|
|
638
730
|
```
|
|
639
|
-
which will download and set up and serve the
|
|
731
|
+
which will download and set up and serve the Incognide application within your `~/.npcsh` folder. It requires `npm` and `node` to work, and of course npcpy !
|
|
640
732
|
|
|
641
733
|
## Mailing List and Community
|
|
642
|
-
Interested to stay in the loop and to hear the latest and greatest about `npcpy`, `npcsh`, and
|
|
734
|
+
Interested to stay in the loop and to hear the latest and greatest about `npcpy`, `npcsh`, and Incognide? Be sure to sign up for the [newsletter](https://forms.gle/n1NzQmwjsV4xv1B2A)!
|
|
643
735
|
|
|
644
736
|
[Join the discord to discuss ideas for npc tools](https://discord.gg/VvYVT5YC)
|
|
645
737
|
## Support
|