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.
Files changed (217) hide show
  1. npcsh/_state.py +138 -100
  2. npcsh/alicanto.py +2 -2
  3. npcsh/benchmark/__init__.py +28 -0
  4. npcsh/benchmark/npcsh_agent.py +296 -0
  5. npcsh/benchmark/runner.py +611 -0
  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 +1 -3
  10. npcsh/conversation_viewer.py +389 -0
  11. npcsh/corca.py +0 -1
  12. npcsh/execution.py +0 -1
  13. npcsh/guac.py +0 -1
  14. npcsh/mcp_helpers.py +2 -3
  15. npcsh/mcp_server.py +5 -10
  16. npcsh/npc.py +10 -11
  17. npcsh/npc_team/jinxs/bin/benchmark.jinx +146 -0
  18. npcsh/npc_team/jinxs/bin/nql.jinx +7 -7
  19. npcsh/npc_team/jinxs/bin/roll.jinx +20 -23
  20. npcsh/npc_team/jinxs/bin/sample.jinx +6 -7
  21. npcsh/npc_team/jinxs/bin/sync.jinx +6 -6
  22. npcsh/npc_team/jinxs/bin/vixynt.jinx +8 -8
  23. npcsh/npc_team/jinxs/incognide/add_tab.jinx +11 -0
  24. npcsh/npc_team/jinxs/incognide/close_pane.jinx +9 -0
  25. npcsh/npc_team/jinxs/incognide/close_tab.jinx +10 -0
  26. npcsh/npc_team/jinxs/incognide/confirm.jinx +10 -0
  27. npcsh/npc_team/jinxs/incognide/focus_pane.jinx +9 -0
  28. npcsh/npc_team/jinxs/{npc_studio/npc-studio.jinx → incognide/incognide.jinx} +2 -2
  29. npcsh/npc_team/jinxs/incognide/list_panes.jinx +8 -0
  30. npcsh/npc_team/jinxs/incognide/navigate.jinx +10 -0
  31. npcsh/npc_team/jinxs/incognide/notify.jinx +10 -0
  32. npcsh/npc_team/jinxs/incognide/open_pane.jinx +13 -0
  33. npcsh/npc_team/jinxs/incognide/read_pane.jinx +9 -0
  34. npcsh/npc_team/jinxs/incognide/run_terminal.jinx +10 -0
  35. npcsh/npc_team/jinxs/incognide/send_message.jinx +10 -0
  36. npcsh/npc_team/jinxs/incognide/split_pane.jinx +12 -0
  37. npcsh/npc_team/jinxs/incognide/switch_npc.jinx +10 -0
  38. npcsh/npc_team/jinxs/incognide/switch_tab.jinx +10 -0
  39. npcsh/npc_team/jinxs/incognide/write_file.jinx +11 -0
  40. npcsh/npc_team/jinxs/incognide/zen_mode.jinx +9 -0
  41. npcsh/npc_team/jinxs/lib/browser/browser_action.jinx +4 -4
  42. npcsh/npc_team/jinxs/lib/browser/browser_screenshot.jinx +1 -1
  43. npcsh/npc_team/jinxs/lib/browser/open_browser.jinx +2 -2
  44. npcsh/npc_team/jinxs/lib/computer_use/click.jinx +2 -2
  45. npcsh/npc_team/jinxs/lib/computer_use/key_press.jinx +1 -1
  46. npcsh/npc_team/jinxs/lib/computer_use/launch_app.jinx +1 -1
  47. npcsh/npc_team/jinxs/lib/computer_use/screenshot.jinx +1 -1
  48. npcsh/npc_team/jinxs/lib/computer_use/trigger.jinx +2 -2
  49. npcsh/npc_team/jinxs/lib/computer_use/type_text.jinx +1 -1
  50. npcsh/npc_team/jinxs/lib/computer_use/wait.jinx +1 -1
  51. npcsh/npc_team/jinxs/lib/core/chat.jinx +4 -4
  52. npcsh/npc_team/jinxs/lib/core/cmd.jinx +4 -4
  53. npcsh/npc_team/jinxs/lib/core/compress.jinx +8 -8
  54. npcsh/npc_team/jinxs/lib/core/edit_file.jinx +3 -0
  55. npcsh/npc_team/jinxs/lib/core/ots.jinx +7 -7
  56. npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +348 -0
  57. npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +339 -0
  58. npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +418 -0
  59. npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +73 -0
  60. npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +388 -0
  61. npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +283 -0
  62. npcsh/npc_team/jinxs/lib/core/search.jinx +52 -129
  63. npcsh/npc_team/jinxs/lib/core/sh.jinx +1 -1
  64. npcsh/npc_team/jinxs/lib/core/sleep.jinx +29 -18
  65. npcsh/npc_team/jinxs/lib/core/sql.jinx +15 -11
  66. npcsh/npc_team/jinxs/lib/orchestration/convene.jinx +7 -7
  67. npcsh/npc_team/jinxs/lib/orchestration/delegate.jinx +8 -9
  68. npcsh/npc_team/jinxs/lib/research/paper_search.jinx +389 -78
  69. npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +373 -56
  70. npcsh/npc_team/jinxs/lib/utils/build.jinx +5 -5
  71. npcsh/npc_team/jinxs/lib/utils/compile.jinx +2 -2
  72. npcsh/npc_team/jinxs/lib/utils/help.jinx +1 -1
  73. npcsh/npc_team/jinxs/lib/utils/init.jinx +5 -5
  74. npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +300 -145
  75. npcsh/npc_team/jinxs/lib/utils/serve.jinx +2 -2
  76. npcsh/npc_team/jinxs/lib/utils/set.jinx +2 -2
  77. npcsh/npc_team/jinxs/lib/utils/switch.jinx +3 -3
  78. npcsh/npc_team/jinxs/lib/utils/switches.jinx +1 -1
  79. npcsh/npc_team/jinxs/lib/utils/teamviz.jinx +2 -2
  80. npcsh/npc_team/jinxs/modes/alicanto.jinx +356 -0
  81. npcsh/npc_team/jinxs/modes/arxiv.jinx +720 -0
  82. npcsh/npc_team/jinxs/modes/corca.jinx +430 -0
  83. npcsh/npc_team/jinxs/modes/guac.jinx +544 -0
  84. npcsh/npc_team/jinxs/modes/plonk.jinx +379 -0
  85. npcsh/npc_team/jinxs/modes/pti.jinx +357 -0
  86. npcsh/npc_team/jinxs/modes/reattach.jinx +291 -0
  87. npcsh/npc_team/jinxs/modes/spool.jinx +350 -0
  88. npcsh/npc_team/jinxs/modes/wander.jinx +455 -0
  89. {npcsh-1.1.16.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/modes}/yap.jinx +8 -2
  90. npcsh/npc_team/sibiji.npc +1 -1
  91. npcsh/npcsh.py +87 -46
  92. npcsh/plonk.py +0 -1
  93. npcsh/pti.py +0 -1
  94. npcsh/routes.py +1 -3
  95. npcsh/spool.py +0 -1
  96. npcsh/ui.py +0 -1
  97. npcsh/wander.py +0 -1
  98. npcsh/yap.py +0 -1
  99. npcsh-1.1.18.data/data/npcsh/npc_team/add_tab.jinx +11 -0
  100. npcsh-1.1.18.data/data/npcsh/npc_team/alicanto.jinx +356 -0
  101. npcsh-1.1.18.data/data/npcsh/npc_team/arxiv.jinx +720 -0
  102. npcsh-1.1.18.data/data/npcsh/npc_team/benchmark.jinx +146 -0
  103. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/browser_action.jinx +4 -4
  104. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/browser_screenshot.jinx +1 -1
  105. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/build.jinx +5 -5
  106. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/chat.jinx +4 -4
  107. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/click.jinx +2 -2
  108. npcsh-1.1.18.data/data/npcsh/npc_team/close_pane.jinx +9 -0
  109. npcsh-1.1.18.data/data/npcsh/npc_team/close_tab.jinx +10 -0
  110. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/cmd.jinx +4 -4
  111. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/compile.jinx +2 -2
  112. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/compress.jinx +8 -8
  113. npcsh-1.1.18.data/data/npcsh/npc_team/confirm.jinx +10 -0
  114. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/convene.jinx +7 -7
  115. npcsh-1.1.18.data/data/npcsh/npc_team/corca.jinx +430 -0
  116. npcsh-1.1.18.data/data/npcsh/npc_team/db_search.jinx +348 -0
  117. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/delegate.jinx +8 -9
  118. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/edit_file.jinx +3 -0
  119. npcsh-1.1.18.data/data/npcsh/npc_team/file_search.jinx +339 -0
  120. npcsh-1.1.18.data/data/npcsh/npc_team/focus_pane.jinx +9 -0
  121. npcsh-1.1.18.data/data/npcsh/npc_team/guac.jinx +544 -0
  122. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/help.jinx +1 -1
  123. 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
  124. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/init.jinx +5 -5
  125. npcsh-1.1.18.data/data/npcsh/npc_team/jinxs.jinx +331 -0
  126. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/key_press.jinx +1 -1
  127. npcsh-1.1.18.data/data/npcsh/npc_team/kg_search.jinx +418 -0
  128. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/launch_app.jinx +1 -1
  129. npcsh-1.1.18.data/data/npcsh/npc_team/list_panes.jinx +8 -0
  130. npcsh-1.1.18.data/data/npcsh/npc_team/mem_review.jinx +73 -0
  131. npcsh-1.1.18.data/data/npcsh/npc_team/mem_search.jinx +388 -0
  132. npcsh-1.1.18.data/data/npcsh/npc_team/navigate.jinx +10 -0
  133. npcsh-1.1.18.data/data/npcsh/npc_team/notify.jinx +10 -0
  134. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/nql.jinx +7 -7
  135. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/open_browser.jinx +2 -2
  136. npcsh-1.1.18.data/data/npcsh/npc_team/open_pane.jinx +13 -0
  137. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/ots.jinx +7 -7
  138. npcsh-1.1.18.data/data/npcsh/npc_team/paper_search.jinx +412 -0
  139. npcsh-1.1.18.data/data/npcsh/npc_team/plonk.jinx +379 -0
  140. npcsh-1.1.18.data/data/npcsh/npc_team/pti.jinx +357 -0
  141. npcsh-1.1.18.data/data/npcsh/npc_team/read_pane.jinx +9 -0
  142. npcsh-1.1.18.data/data/npcsh/npc_team/reattach.jinx +291 -0
  143. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/roll.jinx +20 -23
  144. npcsh-1.1.18.data/data/npcsh/npc_team/run_terminal.jinx +10 -0
  145. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sample.jinx +6 -7
  146. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/screenshot.jinx +1 -1
  147. npcsh-1.1.18.data/data/npcsh/npc_team/search.jinx +54 -0
  148. npcsh-1.1.18.data/data/npcsh/npc_team/semantic_scholar.jinx +386 -0
  149. npcsh-1.1.18.data/data/npcsh/npc_team/send_message.jinx +10 -0
  150. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/serve.jinx +2 -2
  151. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/set.jinx +2 -2
  152. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sh.jinx +1 -1
  153. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sibiji.npc +1 -1
  154. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sleep.jinx +29 -18
  155. npcsh-1.1.18.data/data/npcsh/npc_team/split_pane.jinx +12 -0
  156. npcsh-1.1.18.data/data/npcsh/npc_team/spool.jinx +350 -0
  157. npcsh-1.1.18.data/data/npcsh/npc_team/sql.jinx +20 -0
  158. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/switch.jinx +3 -3
  159. npcsh-1.1.18.data/data/npcsh/npc_team/switch_npc.jinx +10 -0
  160. npcsh-1.1.18.data/data/npcsh/npc_team/switch_tab.jinx +10 -0
  161. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/switches.jinx +1 -1
  162. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sync.jinx +6 -6
  163. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/teamviz.jinx +2 -2
  164. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/trigger.jinx +2 -2
  165. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/type_text.jinx +1 -1
  166. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/vixynt.jinx +8 -8
  167. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/wait.jinx +1 -1
  168. npcsh-1.1.18.data/data/npcsh/npc_team/wander.jinx +455 -0
  169. npcsh-1.1.18.data/data/npcsh/npc_team/web_search.jinx +283 -0
  170. npcsh-1.1.18.data/data/npcsh/npc_team/write_file.jinx +11 -0
  171. {npcsh/npc_team/jinxs/bin → npcsh-1.1.18.data/data/npcsh/npc_team}/yap.jinx +8 -2
  172. npcsh-1.1.18.data/data/npcsh/npc_team/zen_mode.jinx +9 -0
  173. {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/METADATA +99 -7
  174. npcsh-1.1.18.dist-info/RECORD +235 -0
  175. {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/WHEEL +1 -1
  176. {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/entry_points.txt +2 -3
  177. npcsh/npc_team/jinxs/bin/spool.jinx +0 -161
  178. npcsh/npc_team/jinxs/bin/wander.jinx +0 -152
  179. npcsh/npc_team/jinxs/lib/research/arxiv.jinx +0 -76
  180. npcsh-1.1.16.data/data/npcsh/npc_team/arxiv.jinx +0 -76
  181. npcsh-1.1.16.data/data/npcsh/npc_team/jinxs.jinx +0 -176
  182. npcsh-1.1.16.data/data/npcsh/npc_team/paper_search.jinx +0 -101
  183. npcsh-1.1.16.data/data/npcsh/npc_team/search.jinx +0 -131
  184. npcsh-1.1.16.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -69
  185. npcsh-1.1.16.data/data/npcsh/npc_team/spool.jinx +0 -161
  186. npcsh-1.1.16.data/data/npcsh/npc_team/sql.jinx +0 -16
  187. npcsh-1.1.16.data/data/npcsh/npc_team/wander.jinx +0 -152
  188. npcsh-1.1.16.dist-info/RECORD +0 -170
  189. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/alicanto.npc +0 -0
  190. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/alicanto.png +0 -0
  191. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  192. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/corca.npc +0 -0
  193. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/corca.png +0 -0
  194. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/corca_example.png +0 -0
  195. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/frederic.npc +0 -0
  196. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/frederic4.png +0 -0
  197. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/guac.npc +0 -0
  198. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/guac.png +0 -0
  199. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
  200. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  201. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  202. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  203. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  204. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/paste.jinx +0 -0
  205. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonk.npc +0 -0
  206. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonk.png +0 -0
  207. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonkjr.npc +0 -0
  208. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  209. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/python.jinx +0 -0
  210. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/shh.jinx +0 -0
  211. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sibiji.png +0 -0
  212. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/spool.png +0 -0
  213. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/usage.jinx +0 -0
  214. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  215. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/yap.png +0 -0
  216. {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/licenses/LICENSE +0 -0
  217. {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
- 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
  ██╗ ██╗ █████╗ ██████╗
@@ -0,0 +1,9 @@
1
+ jinx_name: studio.zen_mode
2
+ description: Toggle zen mode (fullscreen) for a pane.
3
+ inputs:
4
+ - paneId: "active"
5
+ steps:
6
+ - name: frontend_action
7
+ engine: python
8
+ code: |
9
+ context['output'] = "Action executed by frontend"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: npcsh
3
- Version: 1.1.16
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
- | `/npc-studio` | Launch NPC Studio GUI. Usage: `/npc-studio` |
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
- ## NPC Studio
634
- There is a graphical user interface that makes use of the NPC Toolkit through the NPC Studio. See the source code for NPC Studio [here](https://github.com/npc-worldwide/npc-studio). Download the executables at [our website](https://enpisi.com/downloads). For the most up to date development version, you can use NPC Studio by invoking it in npcsh
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
- /npc-studio
729
+ /incognide
638
730
  ```
639
- which will download and set up and serve the NPC Studio application within your `~/.npcsh` folder. It requires `npm` and `node` to work, and of course npcpy !
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 NPC Studio? Be sure to sign up for the [newsletter](https://forms.gle/n1NzQmwjsV4xv1B2A)!
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