npcsh 1.1.17__py3-none-any.whl → 1.1.19__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.
Files changed (197) hide show
  1. npcsh/_state.py +122 -91
  2. npcsh/alicanto.py +2 -2
  3. npcsh/benchmark/__init__.py +8 -2
  4. npcsh/benchmark/npcsh_agent.py +87 -22
  5. npcsh/benchmark/runner.py +85 -43
  6. npcsh/benchmark/templates/install-npcsh.sh.j2 +35 -0
  7. npcsh/build.py +2 -4
  8. npcsh/completion.py +2 -6
  9. npcsh/config.py +2 -3
  10. npcsh/conversation_viewer.py +389 -0
  11. npcsh/corca.py +0 -1
  12. npcsh/diff_viewer.py +452 -0
  13. npcsh/execution.py +0 -1
  14. npcsh/guac.py +0 -1
  15. npcsh/mcp_helpers.py +2 -3
  16. npcsh/mcp_server.py +5 -10
  17. npcsh/npc.py +10 -11
  18. npcsh/npc_team/jinxs/bin/benchmark.jinx +1 -1
  19. npcsh/npc_team/jinxs/bin/config_tui.jinx +299 -0
  20. npcsh/npc_team/jinxs/bin/memories.jinx +316 -0
  21. npcsh/npc_team/jinxs/bin/setup.jinx +240 -0
  22. npcsh/npc_team/jinxs/bin/sync.jinx +143 -150
  23. npcsh/npc_team/jinxs/bin/team_tui.jinx +327 -0
  24. npcsh/npc_team/jinxs/incognide/add_tab.jinx +1 -1
  25. npcsh/npc_team/jinxs/incognide/close_pane.jinx +1 -1
  26. npcsh/npc_team/jinxs/incognide/close_tab.jinx +1 -1
  27. npcsh/npc_team/jinxs/incognide/confirm.jinx +1 -1
  28. npcsh/npc_team/jinxs/incognide/focus_pane.jinx +1 -1
  29. npcsh/npc_team/jinxs/incognide/list_panes.jinx +1 -1
  30. npcsh/npc_team/jinxs/incognide/navigate.jinx +1 -1
  31. npcsh/npc_team/jinxs/incognide/notify.jinx +1 -1
  32. npcsh/npc_team/jinxs/incognide/open_pane.jinx +1 -1
  33. npcsh/npc_team/jinxs/incognide/read_pane.jinx +1 -1
  34. npcsh/npc_team/jinxs/incognide/run_terminal.jinx +1 -1
  35. npcsh/npc_team/jinxs/incognide/send_message.jinx +1 -1
  36. npcsh/npc_team/jinxs/incognide/split_pane.jinx +1 -1
  37. npcsh/npc_team/jinxs/incognide/switch_npc.jinx +1 -1
  38. npcsh/npc_team/jinxs/incognide/switch_tab.jinx +1 -1
  39. npcsh/npc_team/jinxs/incognide/write_file.jinx +1 -1
  40. npcsh/npc_team/jinxs/incognide/zen_mode.jinx +1 -1
  41. npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +321 -17
  42. npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +312 -67
  43. npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +366 -44
  44. npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +73 -0
  45. npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +328 -20
  46. npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +242 -10
  47. npcsh/npc_team/jinxs/lib/core/sleep.jinx +22 -11
  48. npcsh/npc_team/jinxs/lib/core/sql.jinx +10 -6
  49. npcsh/npc_team/jinxs/lib/research/paper_search.jinx +387 -76
  50. npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +372 -55
  51. npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +299 -144
  52. npcsh/npc_team/jinxs/modes/alicanto.jinx +356 -0
  53. npcsh/npc_team/jinxs/modes/arxiv.jinx +720 -0
  54. npcsh/npc_team/jinxs/modes/corca.jinx +430 -0
  55. npcsh/npc_team/jinxs/modes/guac.jinx +542 -0
  56. npcsh/npc_team/jinxs/modes/plonk.jinx +379 -0
  57. npcsh/npc_team/jinxs/modes/pti.jinx +357 -0
  58. npcsh/npc_team/jinxs/modes/reattach.jinx +291 -0
  59. npcsh/npc_team/jinxs/modes/spool.jinx +350 -0
  60. npcsh/npc_team/jinxs/modes/wander.jinx +455 -0
  61. npcsh/npc_team/jinxs/{bin → modes}/yap.jinx +13 -7
  62. npcsh/npcsh.py +7 -4
  63. npcsh/plonk.py +0 -1
  64. npcsh/pti.py +0 -1
  65. npcsh/routes.py +1 -3
  66. npcsh/spool.py +0 -1
  67. npcsh/ui.py +0 -1
  68. npcsh/wander.py +0 -1
  69. npcsh/yap.py +0 -1
  70. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/add_tab.jinx +1 -1
  71. npcsh-1.1.19.data/data/npcsh/npc_team/alicanto.jinx +356 -0
  72. npcsh-1.1.19.data/data/npcsh/npc_team/arxiv.jinx +720 -0
  73. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/benchmark.jinx +1 -1
  74. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/close_pane.jinx +1 -1
  75. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/close_tab.jinx +1 -1
  76. npcsh-1.1.19.data/data/npcsh/npc_team/config_tui.jinx +299 -0
  77. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/confirm.jinx +1 -1
  78. npcsh-1.1.19.data/data/npcsh/npc_team/corca.jinx +430 -0
  79. npcsh-1.1.19.data/data/npcsh/npc_team/db_search.jinx +348 -0
  80. npcsh-1.1.19.data/data/npcsh/npc_team/file_search.jinx +339 -0
  81. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/focus_pane.jinx +1 -1
  82. npcsh-1.1.19.data/data/npcsh/npc_team/guac.jinx +542 -0
  83. npcsh-1.1.19.data/data/npcsh/npc_team/jinxs.jinx +331 -0
  84. npcsh-1.1.19.data/data/npcsh/npc_team/kg_search.jinx +418 -0
  85. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/list_panes.jinx +1 -1
  86. npcsh-1.1.19.data/data/npcsh/npc_team/mem_review.jinx +73 -0
  87. npcsh-1.1.19.data/data/npcsh/npc_team/mem_search.jinx +388 -0
  88. npcsh-1.1.19.data/data/npcsh/npc_team/memories.jinx +316 -0
  89. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/navigate.jinx +1 -1
  90. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/notify.jinx +1 -1
  91. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/open_pane.jinx +1 -1
  92. npcsh-1.1.19.data/data/npcsh/npc_team/paper_search.jinx +412 -0
  93. npcsh-1.1.19.data/data/npcsh/npc_team/plonk.jinx +379 -0
  94. npcsh-1.1.19.data/data/npcsh/npc_team/pti.jinx +357 -0
  95. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/read_pane.jinx +1 -1
  96. npcsh-1.1.19.data/data/npcsh/npc_team/reattach.jinx +291 -0
  97. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/run_terminal.jinx +1 -1
  98. npcsh-1.1.19.data/data/npcsh/npc_team/semantic_scholar.jinx +386 -0
  99. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/send_message.jinx +1 -1
  100. npcsh-1.1.19.data/data/npcsh/npc_team/setup.jinx +240 -0
  101. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/sleep.jinx +22 -11
  102. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/split_pane.jinx +1 -1
  103. npcsh-1.1.19.data/data/npcsh/npc_team/spool.jinx +350 -0
  104. npcsh-1.1.19.data/data/npcsh/npc_team/sql.jinx +20 -0
  105. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/switch_npc.jinx +1 -1
  106. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/switch_tab.jinx +1 -1
  107. npcsh-1.1.19.data/data/npcsh/npc_team/sync.jinx +223 -0
  108. npcsh-1.1.19.data/data/npcsh/npc_team/team_tui.jinx +327 -0
  109. npcsh-1.1.19.data/data/npcsh/npc_team/wander.jinx +455 -0
  110. npcsh-1.1.19.data/data/npcsh/npc_team/web_search.jinx +283 -0
  111. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/write_file.jinx +1 -1
  112. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/yap.jinx +13 -7
  113. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/zen_mode.jinx +1 -1
  114. {npcsh-1.1.17.dist-info → npcsh-1.1.19.dist-info}/METADATA +110 -14
  115. npcsh-1.1.19.dist-info/RECORD +244 -0
  116. {npcsh-1.1.17.dist-info → npcsh-1.1.19.dist-info}/WHEEL +1 -1
  117. {npcsh-1.1.17.dist-info → npcsh-1.1.19.dist-info}/entry_points.txt +4 -3
  118. npcsh/npc_team/jinxs/bin/spool.jinx +0 -161
  119. npcsh/npc_team/jinxs/bin/wander.jinx +0 -242
  120. npcsh/npc_team/jinxs/lib/research/arxiv.jinx +0 -76
  121. npcsh-1.1.17.data/data/npcsh/npc_team/arxiv.jinx +0 -76
  122. npcsh-1.1.17.data/data/npcsh/npc_team/db_search.jinx +0 -44
  123. npcsh-1.1.17.data/data/npcsh/npc_team/file_search.jinx +0 -94
  124. npcsh-1.1.17.data/data/npcsh/npc_team/jinxs.jinx +0 -176
  125. npcsh-1.1.17.data/data/npcsh/npc_team/kg_search.jinx +0 -96
  126. npcsh-1.1.17.data/data/npcsh/npc_team/mem_search.jinx +0 -80
  127. npcsh-1.1.17.data/data/npcsh/npc_team/paper_search.jinx +0 -101
  128. npcsh-1.1.17.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -69
  129. npcsh-1.1.17.data/data/npcsh/npc_team/spool.jinx +0 -161
  130. npcsh-1.1.17.data/data/npcsh/npc_team/sql.jinx +0 -16
  131. npcsh-1.1.17.data/data/npcsh/npc_team/sync.jinx +0 -230
  132. npcsh-1.1.17.data/data/npcsh/npc_team/wander.jinx +0 -242
  133. npcsh-1.1.17.data/data/npcsh/npc_team/web_search.jinx +0 -51
  134. npcsh-1.1.17.dist-info/RECORD +0 -219
  135. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/alicanto.npc +0 -0
  136. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/alicanto.png +0 -0
  137. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
  138. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
  139. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/build.jinx +0 -0
  140. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/chat.jinx +0 -0
  141. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/click.jinx +0 -0
  142. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  143. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/cmd.jinx +0 -0
  144. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/compile.jinx +0 -0
  145. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/compress.jinx +0 -0
  146. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/convene.jinx +0 -0
  147. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/corca.npc +0 -0
  148. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/corca.png +0 -0
  149. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/corca_example.png +0 -0
  150. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/delegate.jinx +0 -0
  151. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/edit_file.jinx +0 -0
  152. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/frederic.npc +0 -0
  153. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/frederic4.png +0 -0
  154. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/guac.npc +0 -0
  155. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/guac.png +0 -0
  156. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/help.jinx +0 -0
  157. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/incognide.jinx +0 -0
  158. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/init.jinx +0 -0
  159. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
  160. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  161. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/key_press.jinx +0 -0
  162. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
  163. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  164. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  165. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  166. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/nql.jinx +0 -0
  167. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
  168. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/ots.jinx +0 -0
  169. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/paste.jinx +0 -0
  170. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/plonk.npc +0 -0
  171. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/plonk.png +0 -0
  172. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/plonkjr.npc +0 -0
  173. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  174. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/python.jinx +0 -0
  175. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/roll.jinx +0 -0
  176. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/sample.jinx +0 -0
  177. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
  178. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/search.jinx +0 -0
  179. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/serve.jinx +0 -0
  180. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/set.jinx +0 -0
  181. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/sh.jinx +0 -0
  182. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/shh.jinx +0 -0
  183. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/sibiji.npc +0 -0
  184. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/sibiji.png +0 -0
  185. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/spool.png +0 -0
  186. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/switch.jinx +0 -0
  187. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/switches.jinx +0 -0
  188. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
  189. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/trigger.jinx +0 -0
  190. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/type_text.jinx +0 -0
  191. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/usage.jinx +0 -0
  192. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  193. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/vixynt.jinx +0 -0
  194. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/wait.jinx +0 -0
  195. {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/yap.png +0 -0
  196. {npcsh-1.1.17.dist-info → npcsh-1.1.19.dist-info}/licenses/LICENSE +0 -0
  197. {npcsh-1.1.17.dist-info → npcsh-1.1.19.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()
@@ -1,4 +1,4 @@
1
- jinx_name: studio.write_file
1
+ jinx_name: studio_write_file
2
2
  description: Write content to an editor pane. Updates the file content in the pane.
3
3
  inputs:
4
4
  - paneId: "active"
@@ -1,11 +1,11 @@
1
1
  jinx_name: yap
2
2
  description: Voice chat mode - speech-to-text input, text-to-speech output
3
3
  inputs:
4
- - model: null
5
- - provider: null
6
- - tts_model: kokoro
7
- - voice: af_heart
8
- - files: null
4
+ - model: null
5
+ - provider: null
6
+ - tts_model: kokoro
7
+ - voice: af_heart
8
+ - files: null
9
9
 
10
10
  steps:
11
11
  - name: yap_repl
@@ -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
- model = context.get('model') or (npc.model if npc else None)
53
- provider = context.get('provider') or (npc.provider if npc else None)
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,4 +1,4 @@
1
- jinx_name: studio.zen_mode
1
+ jinx_name: studio_zen_mode
2
2
  description: Toggle zen mode (fullscreen) for a pane.
3
3
  inputs:
4
4
  - paneId: "active"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: npcsh
3
- Version: 1.1.17
3
+ Version: 1.1.19
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
@@ -106,31 +106,38 @@ Dynamic: summary
106
106
  <img src="https://raw.githubusercontent.com/NPC-Worldwide/npcsh/main/npcsh/npcsh.png" alt="npcsh logo" width=600></a>
107
107
  </p>
108
108
 
109
- # NPC Shell
109
+ # npcsh
110
110
 
111
- `npcsh` - a new standard in human-agent interaction. Co-create with agents to build organizations that refine themselves and evolve to your needs.
112
-
113
- The NPC shell is a suite of programs to make use of multi-modal LLMs and agents in novel interactive modes. Based in the command line, use it wherever you work.
114
-
115
- - It is developed to work reliably with small models and performs excellently with the state-of-the-art models from major model providers.
116
- - Fundamentally, the core program of npcsh extends the familiar bash environment with an intelligent layer that lets users seamlessly ask agents questions, run pre-built or custom macros or agents, all without breaking the flow of command-line work.
117
- - Switching between agents is a breeze in `npcsh`, letting you quickly and easily take advantage of a variety of agents (e.g. coding agents versus tool-calling agents versus prompt-based ReACT Flow agents) and personas (e.g. Data scientist, mapmaker with ennui, etc.).
118
- - Project variables and context can be stored in team `.ctx` files. Personas (`.npc`) and Jinja execution templates (`.jinx`) are likewise stored in `yaml` within the global `npcsh` team or your project-specific one, letting you focus on adjusting and engineering context and system prompts iteratively so you can constantly improve your agent team's performance.
111
+ The NPC shell (`npcsh`) makes the most of multi-modal LLMs and agents through a powerful set of simple slash commands and novel interactive modes, all from the comfort of the command line. Build teams of agents and schedule them on jobs, engineer context, and design custom interaction modes and Jinja Execution templates (Jinxs for you and your agents to invoke, all managed scalably for organizations of any size through the NPC data layer.
119
112
 
120
113
  To get started:
114
+ For users who want to mainly use models through APIs (`ollama`, `gemini`, `kimi`, `grok`, `deepseek`, `anthropic`, `openai`, `mistral`, or any others provided by litellm )
121
115
  ```bash
122
- # for users who want to mainly use models through APIs (e.g. , gemini, grok, deepseek, anthropic, openai, mistral, , any others provided by litellm ):
123
116
  pip install 'npcsh[lite]'
124
- # for users who want to use local models (these install diffusers/transformers/torch stack so it is big.):
117
+ ```
118
+ For users who want to use and fine-tune local models (this installs `diffusers`/`transformers`/`torch` stack so it is big):
119
+
120
+ ```bash
125
121
  pip install 'npcsh[local]'
126
- # for users who want to use the voice mode `yap`, see also the OS-specific installation instructions for installing needed system audio libraries
122
+ ```
123
+
124
+
125
+ For users who want to use the voice mode `yap` (see also the OS-specific installation instructions for installing needed system audio libraries)
126
+ ```bash
127
127
  pip install 'npcsh[yap]'
128
128
  ```
129
+
129
130
  Once installed: run
130
131
  ```bash
131
132
  npcsh
132
133
  ```
133
- and you will enter the NPC shell. Additionally, the pip installation includes the following CLI tools available in bash: `npc` cli, `wander`, `spool`, `yap`, and `nql`. Bin jinxs in `npc_team/jinxs/bin/` are automatically registered as CLI commands.
134
+ and you will enter the NPC shell.
135
+
136
+ If you do not have any local models
137
+
138
+
139
+
140
+ Additionally, the pip installation includes the following CLI tools available in bash: `npc` cli, `wander`, `spool`, `yap`, and `nql`. Bin jinxs in `npc_team/jinxs/bin/` are automatically registered as CLI commands.
134
141
 
135
142
 
136
143
  # Usage
@@ -596,6 +603,9 @@ npc vixynt "a sunset over mountains"
596
603
  | `/teamviz` | Visualize team structure. Usage: `/teamviz save=output.png` |
597
604
  | `/ots` | Screenshot analysis. Usage: `/ots` then select area |
598
605
  | `/sleep` | Evolve knowledge graph. Usage: `/sleep --ops link_facts,deepen` |
606
+ | `/kg_search` | Search knowledge graph with multiple modes. Usage: `/kg_search query mode=hybrid depth=2` |
607
+ | `/mem_search` | Search approved memories. Usage: `/mem_search query status=approved top_k=10` |
608
+ | `/mem_review` | Review pending memories interactively. Usage: `/mem_review limit=50` |
599
609
 
600
610
  ### System & Config
601
611
  | Command | Description |
@@ -626,6 +636,92 @@ npc vixynt "a sunset over mountains"
626
636
  --format (-f) | --num_frames (-num_f) | --sprovider (-s) |
627
637
  ```
628
638
 
639
+ ## Memory & Knowledge Graph
640
+
641
+ `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.
642
+
643
+ ### Memory Lifecycle
644
+
645
+ Memories are extracted from conversations and follow this lifecycle:
646
+
647
+ 1. **pending_approval** - New memories awaiting review
648
+ 2. **human-approved** - Approved and ready for KG integration
649
+ 3. **human-rejected** - Rejected (used as negative examples)
650
+ 4. **human-edited** - Modified by user before approval
651
+ 5. **skipped** - Deferred for later review
652
+
653
+ ### Memory Commands
654
+
655
+ ```bash
656
+ # Search through approved memories
657
+ /mem_search python # Keyword search
658
+ /mem_search python status=approved # Filter by status
659
+ /mem_search python top_k=20 # Limit results
660
+
661
+ # Review pending memories interactively
662
+ /mem_review # Review with default limit
663
+ /mem_review limit=50 # Review more at once
664
+ ```
665
+
666
+ ### Knowledge Graph
667
+
668
+ 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.
669
+
670
+ ```bash
671
+ # Keyword search
672
+ /kg_search python # Simple keyword match
673
+
674
+ # Semantic similarity search
675
+ /kg_search python mode=embedding # Find semantically similar facts
676
+
677
+ # Graph traversal search
678
+ /kg_search python mode=link depth=3 # Traverse graph links
679
+
680
+ # Hybrid search (combines methods)
681
+ /kg_search python mode=all # All methods combined
682
+
683
+ # Explore concepts
684
+ /kg_search type=concepts # List all concepts
685
+ /kg_search concept="Machine Learning" # Explore a specific concept
686
+ ```
687
+
688
+ ### Knowledge Graph Evolution
689
+
690
+ The `/sleep` command evolves the knowledge graph through consolidation, abstraction, and creative synthesis:
691
+
692
+ ```bash
693
+ # Basic sleep (consolidation)
694
+ /sleep
695
+
696
+ # Import approved memories first, then evolve
697
+ /sleep backfill=true
698
+
699
+ # Dream mode - creative synthesis across domains
700
+ /sleep dream=true
701
+
702
+ # Combined backfill and dream
703
+ /sleep backfill=true dream=true
704
+
705
+ # Specific operations
706
+ /sleep ops=prune,deepen,abstract
707
+ ```
708
+
709
+ **Operations:**
710
+ - **prune** - Remove redundant or low-value facts
711
+ - **deepen** - Add detail to existing facts
712
+ - **abstract** - Create higher-level generalizations
713
+ - **link** - Connect related facts and concepts
714
+
715
+ ### Environment Variables
716
+
717
+ ```bash
718
+ # Enable/disable automatic KG building (default: enabled)
719
+ export NPCSH_BUILD_KG=1
720
+
721
+ # Database path
722
+ export NPCSH_DB_PATH=~/npcsh_history.db
723
+ ```
724
+
629
725
  ## Read the Docs
630
726
  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/)
631
727