npcsh 1.1.19__py3-none-any.whl → 1.1.21__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 (173) hide show
  1. npcsh/_state.py +16 -78
  2. npcsh/diff_viewer.py +3 -3
  3. npcsh/npc_team/jinxs/lib/core/compress.jinx +373 -85
  4. npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +18 -7
  5. npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +18 -7
  6. npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +20 -9
  7. npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +53 -15
  8. npcsh/npc_team/jinxs/{bin → lib/utils}/benchmark.jinx +2 -2
  9. npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +393 -317
  10. npcsh/npc_team/jinxs/lib/utils/models.jinx +343 -0
  11. npcsh/npc_team/jinxs/{bin → lib/utils}/setup.jinx +8 -7
  12. npcsh/npc_team/jinxs/modes/alicanto.jinx +1573 -296
  13. npcsh/npc_team/jinxs/modes/arxiv.jinx +6 -6
  14. npcsh/npc_team/jinxs/modes/config_tui.jinx +300 -0
  15. npcsh/npc_team/jinxs/modes/corca.jinx +4 -4
  16. npcsh/npc_team/jinxs/modes/git.jinx +795 -0
  17. npcsh/npc_team/jinxs/modes/guac.jinx +4 -4
  18. npcsh/npc_team/jinxs/modes/kg.jinx +941 -0
  19. npcsh/npc_team/jinxs/modes/memories.jinx +414 -0
  20. npcsh/npc_team/jinxs/modes/nql.jinx +460 -0
  21. npcsh/npc_team/jinxs/modes/papers.jinx +578 -0
  22. npcsh/npc_team/jinxs/modes/plonk.jinx +490 -304
  23. npcsh/npc_team/jinxs/modes/pti.jinx +1 -1
  24. npcsh/npc_team/jinxs/modes/reattach.jinx +4 -4
  25. npcsh/npc_team/jinxs/modes/spool.jinx +4 -4
  26. npcsh/npc_team/jinxs/modes/team.jinx +504 -0
  27. npcsh/npc_team/jinxs/modes/vixynt.jinx +388 -0
  28. npcsh/npc_team/jinxs/modes/wander.jinx +455 -182
  29. npcsh/npc_team/jinxs/modes/yap.jinx +10 -3
  30. npcsh/npcsh.py +112 -47
  31. npcsh/routes.py +12 -3
  32. npcsh/salmon_simulation.py +0 -0
  33. npcsh-1.1.21.data/data/npcsh/npc_team/alicanto.jinx +1633 -0
  34. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/arxiv.jinx +6 -6
  35. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/benchmark.jinx +2 -2
  36. npcsh-1.1.21.data/data/npcsh/npc_team/compress.jinx +428 -0
  37. npcsh-1.1.21.data/data/npcsh/npc_team/config_tui.jinx +300 -0
  38. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.jinx +4 -4
  39. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/db_search.jinx +18 -7
  40. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/file_search.jinx +18 -7
  41. npcsh-1.1.21.data/data/npcsh/npc_team/git.jinx +795 -0
  42. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.jinx +4 -4
  43. npcsh-1.1.21.data/data/npcsh/npc_team/jinxs.jinx +407 -0
  44. npcsh-1.1.21.data/data/npcsh/npc_team/kg.jinx +941 -0
  45. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kg_search.jinx +20 -9
  46. npcsh-1.1.21.data/data/npcsh/npc_team/memories.jinx +414 -0
  47. npcsh-1.1.21.data/data/npcsh/npc_team/models.jinx +343 -0
  48. npcsh-1.1.21.data/data/npcsh/npc_team/nql.jinx +460 -0
  49. npcsh-1.1.21.data/data/npcsh/npc_team/papers.jinx +578 -0
  50. npcsh-1.1.21.data/data/npcsh/npc_team/plonk.jinx +565 -0
  51. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/pti.jinx +1 -1
  52. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/reattach.jinx +4 -4
  53. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/setup.jinx +8 -7
  54. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/spool.jinx +4 -4
  55. npcsh-1.1.21.data/data/npcsh/npc_team/team.jinx +504 -0
  56. npcsh-1.1.21.data/data/npcsh/npc_team/vixynt.jinx +388 -0
  57. npcsh-1.1.21.data/data/npcsh/npc_team/wander.jinx +728 -0
  58. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/web_search.jinx +53 -15
  59. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/yap.jinx +10 -3
  60. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/METADATA +2 -2
  61. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/RECORD +147 -148
  62. npcsh-1.1.21.dist-info/entry_points.txt +11 -0
  63. npcsh/npc_team/jinxs/bin/config_tui.jinx +0 -299
  64. npcsh/npc_team/jinxs/bin/memories.jinx +0 -316
  65. npcsh/npc_team/jinxs/bin/nql.jinx +0 -141
  66. npcsh/npc_team/jinxs/bin/team_tui.jinx +0 -327
  67. npcsh/npc_team/jinxs/bin/vixynt.jinx +0 -122
  68. npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +0 -73
  69. npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +0 -388
  70. npcsh/npc_team/jinxs/lib/research/paper_search.jinx +0 -412
  71. npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +0 -386
  72. npcsh/npc_team/plonkjr.npc +0 -23
  73. npcsh-1.1.19.data/data/npcsh/npc_team/alicanto.jinx +0 -356
  74. npcsh-1.1.19.data/data/npcsh/npc_team/compress.jinx +0 -140
  75. npcsh-1.1.19.data/data/npcsh/npc_team/config_tui.jinx +0 -299
  76. npcsh-1.1.19.data/data/npcsh/npc_team/jinxs.jinx +0 -331
  77. npcsh-1.1.19.data/data/npcsh/npc_team/mem_review.jinx +0 -73
  78. npcsh-1.1.19.data/data/npcsh/npc_team/mem_search.jinx +0 -388
  79. npcsh-1.1.19.data/data/npcsh/npc_team/memories.jinx +0 -316
  80. npcsh-1.1.19.data/data/npcsh/npc_team/nql.jinx +0 -141
  81. npcsh-1.1.19.data/data/npcsh/npc_team/paper_search.jinx +0 -412
  82. npcsh-1.1.19.data/data/npcsh/npc_team/plonk.jinx +0 -379
  83. npcsh-1.1.19.data/data/npcsh/npc_team/plonkjr.npc +0 -23
  84. npcsh-1.1.19.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -386
  85. npcsh-1.1.19.data/data/npcsh/npc_team/team_tui.jinx +0 -327
  86. npcsh-1.1.19.data/data/npcsh/npc_team/vixynt.jinx +0 -122
  87. npcsh-1.1.19.data/data/npcsh/npc_team/wander.jinx +0 -455
  88. npcsh-1.1.19.dist-info/entry_points.txt +0 -22
  89. /npcsh/npc_team/jinxs/lib/{orchestration → core}/convene.jinx +0 -0
  90. /npcsh/npc_team/jinxs/lib/{orchestration → core}/delegate.jinx +0 -0
  91. /npcsh/npc_team/jinxs/{bin → lib/core}/sample.jinx +0 -0
  92. /npcsh/npc_team/jinxs/{bin → lib/utils}/sync.jinx +0 -0
  93. /npcsh/npc_team/jinxs/{bin → modes}/roll.jinx +0 -0
  94. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/add_tab.jinx +0 -0
  95. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/alicanto.npc +0 -0
  96. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/alicanto.png +0 -0
  97. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
  98. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
  99. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/build.jinx +0 -0
  100. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/chat.jinx +0 -0
  101. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/click.jinx +0 -0
  102. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  103. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_pane.jinx +0 -0
  104. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_tab.jinx +0 -0
  105. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/cmd.jinx +0 -0
  106. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/compile.jinx +0 -0
  107. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/confirm.jinx +0 -0
  108. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/convene.jinx +0 -0
  109. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.npc +0 -0
  110. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.png +0 -0
  111. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca_example.png +0 -0
  112. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/delegate.jinx +0 -0
  113. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/edit_file.jinx +0 -0
  114. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/focus_pane.jinx +0 -0
  115. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/frederic.npc +0 -0
  116. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/frederic4.png +0 -0
  117. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.npc +0 -0
  118. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.png +0 -0
  119. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/help.jinx +0 -0
  120. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/incognide.jinx +0 -0
  121. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/init.jinx +0 -0
  122. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
  123. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  124. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/key_press.jinx +0 -0
  125. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
  126. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/list_panes.jinx +0 -0
  127. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  128. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/navigate.jinx +0 -0
  129. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/notify.jinx +0 -0
  130. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  131. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  132. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
  133. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/open_pane.jinx +0 -0
  134. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/ots.jinx +0 -0
  135. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/paste.jinx +0 -0
  136. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonk.npc +0 -0
  137. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonk.png +0 -0
  138. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  139. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/python.jinx +0 -0
  140. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/read_pane.jinx +0 -0
  141. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/roll.jinx +0 -0
  142. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/run_terminal.jinx +0 -0
  143. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sample.jinx +0 -0
  144. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
  145. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/search.jinx +0 -0
  146. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/send_message.jinx +0 -0
  147. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/serve.jinx +0 -0
  148. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/set.jinx +0 -0
  149. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sh.jinx +0 -0
  150. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/shh.jinx +0 -0
  151. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sibiji.npc +0 -0
  152. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sibiji.png +0 -0
  153. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sleep.jinx +0 -0
  154. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/split_pane.jinx +0 -0
  155. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/spool.png +0 -0
  156. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sql.jinx +0 -0
  157. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch.jinx +0 -0
  158. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch_npc.jinx +0 -0
  159. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch_tab.jinx +0 -0
  160. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switches.jinx +0 -0
  161. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sync.jinx +0 -0
  162. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
  163. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/trigger.jinx +0 -0
  164. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/type_text.jinx +0 -0
  165. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/usage.jinx +0 -0
  166. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  167. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/wait.jinx +0 -0
  168. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/write_file.jinx +0 -0
  169. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/yap.png +0 -0
  170. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/zen_mode.jinx +0 -0
  171. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/WHEEL +0 -0
  172. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/licenses/LICENSE +0 -0
  173. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  jinx_name: db_search
2
2
  description: Search conversation history database with interactive TUI
3
+ interactive: true
3
4
  inputs:
4
5
  - query: ""
5
6
  - path: ""
@@ -179,7 +180,7 @@ steps:
179
180
  header = f" DB SEARCH ({len(display_rows)} results): '{query}' [sort:{sort_mode}({sort_ind}) filter:{role_filter}] "
180
181
  else:
181
182
  header = f" PREVIEW: {display_rows[selected]['conversation_id'][:16]} "
182
- sys.stdout.write(f'\033[44;37;1m{header.ljust(width)}\033[0m\n')
183
+ sys.stdout.write(f'\033[7;1m{header.ljust(width)}\033[0m\n')
183
184
 
184
185
  # Column headers
185
186
  if mode == 'list':
@@ -213,7 +214,7 @@ steps:
213
214
  line = line[:width+20] # allow for color codes
214
215
 
215
216
  if idx == selected:
216
- sys.stdout.write(f'\033[47;30;1m>{line}\033[0m')
217
+ sys.stdout.write(f'\033[7;1m>{line}\033[0m')
217
218
  else:
218
219
  sys.stdout.write(f' {line}')
219
220
 
@@ -225,7 +226,7 @@ steps:
225
226
  if len(path) > 40:
226
227
  path = '...' + path[-37:]
227
228
  sys.stdout.write(f'\033[{height-1};1H\033[K {cid} @ {path}'.ljust(width))
228
- sys.stdout.write(f'\033[{height};1H\033[K\033[44;37m j/k:Nav 1/2/3:Sort f:Filter p:Preview r:Reattach q:Quit [{selected+1}/{len(display_rows)}] \033[0m')
229
+ sys.stdout.write(f'\033[{height};1H\033[K\033[7m j/k:Nav 1/2/3:Sort f:Filter p:Preview r:Reattach q:Quit [{selected+1}/{len(display_rows)}] \033[0m')
229
230
 
230
231
  else: # preview mode
231
232
  sel = display_rows[selected]
@@ -243,16 +244,26 @@ steps:
243
244
  npc_name = sel.get('npc') or 'default'
244
245
  ts = format_ts(sel.get('timestamp'))
245
246
  sys.stdout.write(f'\033[{height-1};1H\033[K {role}/{npc_name} - {ts} [{preview_scroll+1}/{len(lines)} lines]')
246
- sys.stdout.write(f'\033[{height};1H\033[K\033[44;37m j/k:Scroll b:Back r:Reattach q:Quit \033[0m')
247
+ sys.stdout.write(f'\033[{height};1H\033[K\033[7m j/k:Scroll b:Back r:Reattach q:Quit \033[0m')
247
248
 
248
249
  sys.stdout.flush()
249
250
 
250
- c = sys.stdin.read(1)
251
+ c = os.read(fd, 1).decode('latin-1')
251
252
 
252
253
  if c == '\x1b':
253
- c2 = sys.stdin.read(1)
254
+ import select as _sel
255
+ if _sel.select([fd], [], [], 0.05)[0]:
256
+ c2 = os.read(fd, 1).decode('latin-1')
257
+ else:
258
+ if mode == 'preview':
259
+ mode = 'list'
260
+ sys.stdout.write('\033[2J\033[H')
261
+ else:
262
+ context['output'] = "Cancelled."
263
+ break
264
+ continue
254
265
  if c2 == '[':
255
- c3 = sys.stdin.read(1)
266
+ c3 = os.read(fd, 1).decode('latin-1')
256
267
  if c3 == 'A': # Up
257
268
  if mode == 'list' and selected > 0:
258
269
  selected -= 1
@@ -1,5 +1,6 @@
1
1
  jinx_name: file_search
2
2
  description: Find and browse files with interactive TUI
3
+ interactive: true
3
4
  inputs:
4
5
  - pattern: ""
5
6
  - path: "."
@@ -176,7 +177,7 @@ steps:
176
177
  header = f" FILES ({len(display_files)}): '{pattern}' [sort:{sort_mode}({sort_ind})] "
177
178
  else:
178
179
  header = f" PREVIEW: {display_files[selected]['name']} "
179
- sys.stdout.write(f'\033[44;37;1m{header.ljust(width)}\033[0m\n')
180
+ sys.stdout.write(f'\033[7;1m{header.ljust(width)}\033[0m\n')
180
181
 
181
182
  # Column headers
182
183
  if mode == 'list':
@@ -207,7 +208,7 @@ steps:
207
208
  line = line[:width-1]
208
209
 
209
210
  if idx == selected:
210
- sys.stdout.write(f'\033[47;30;1m>{line}\033[0m')
211
+ sys.stdout.write(f'\033[7;1m>{line}\033[0m')
211
212
  else:
212
213
  sys.stdout.write(f' {line}')
213
214
 
@@ -216,7 +217,7 @@ steps:
216
217
  f = display_files[selected] if display_files else {}
217
218
  full_path = f.get('path', '')
218
219
  sys.stdout.write(f'\033[{height-1};1H\033[K {full_path}'.ljust(width)[:width])
219
- sys.stdout.write(f'\033[{height};1H\033[K\033[44;37m j/k:Nav 1/2/3:Sort p:Preview o:Edit i:Incog c:Copy q:Quit [{selected+1}/{len(display_files)}] \033[0m')
220
+ sys.stdout.write(f'\033[{height};1H\033[K\033[7m j/k:Nav 1/2/3:Sort p:Preview o:Edit i:Incog c:Copy q:Quit [{selected+1}/{len(display_files)}] \033[0m')
220
221
 
221
222
  elif mode == 'preview':
222
223
  for i in range(list_height):
@@ -227,16 +228,26 @@ steps:
227
228
 
228
229
  sys.stdout.write(f'\033[{height-2};1H\033[K\033[90m{"─" * width}\033[0m')
229
230
  sys.stdout.write(f'\033[{height-1};1H\033[K [{preview_scroll+1}/{len(preview_lines)} lines]')
230
- sys.stdout.write(f'\033[{height};1H\033[K\033[44;37m j/k:Scroll b:Back o:Edit q:Quit \033[0m')
231
+ sys.stdout.write(f'\033[{height};1H\033[K\033[7m j/k:Scroll b:Back o:Edit q:Quit \033[0m')
231
232
 
232
233
  sys.stdout.flush()
233
234
 
234
- c = sys.stdin.read(1)
235
+ c = os.read(fd, 1).decode('latin-1')
235
236
 
236
237
  if c == '\x1b':
237
- c2 = sys.stdin.read(1)
238
+ import select as _sel
239
+ if _sel.select([fd], [], [], 0.05)[0]:
240
+ c2 = os.read(fd, 1).decode('latin-1')
241
+ else:
242
+ if mode == 'preview':
243
+ mode = 'list'
244
+ sys.stdout.write('\033[2J\033[H')
245
+ else:
246
+ context['output'] = "Cancelled."
247
+ break
248
+ continue
238
249
  if c2 == '[':
239
- c3 = sys.stdin.read(1)
250
+ c3 = os.read(fd, 1).decode('latin-1')
240
251
  if c3 == 'A':
241
252
  if mode == 'list' and selected > 0:
242
253
  selected -= 1
@@ -1,5 +1,6 @@
1
1
  jinx_name: kg_search
2
2
  description: Search the knowledge graph with interactive TUI
3
+ interactive: true
3
4
  inputs:
4
5
  - query: ""
5
6
  - type: "facts"
@@ -241,11 +242,11 @@ steps:
241
242
  # Header
242
243
  if mode == 'list':
243
244
  sort_ind = {'score': '1', 'concept': '2', 'type': '3'}[sort_mode]
244
- q = query or concept or search_type
245
- header = f" KG SEARCH ({len(display_results)} results): '{q}' [sort:{sort_mode}({sort_ind}) filter:{type_filter}] "
245
+ label = query or concept or search_type
246
+ header = f" KG SEARCH ({len(display_results)} results): '{label}' [sort:{sort_mode}({sort_ind}) filter:{type_filter}] "
246
247
  else:
247
248
  header = f" PREVIEW: {display_results[selected]['type']} "
248
- sys.stdout.write(f'\033[44;37;1m{header.ljust(width)}\033[0m\n')
249
+ sys.stdout.write(f'\033[7;1m{header.ljust(width)}\033[0m\n')
249
250
 
250
251
  # Column headers
251
252
  if mode == 'list':
@@ -277,7 +278,7 @@ steps:
277
278
  line = line[:width+15]
278
279
 
279
280
  if idx == selected:
280
- sys.stdout.write(f'\033[47;30;1m>{line}\033[0m')
281
+ sys.stdout.write(f'\033[7;1m>{line}\033[0m')
281
282
  else:
282
283
  sys.stdout.write(f' {line}')
283
284
 
@@ -286,7 +287,7 @@ steps:
286
287
  sel = display_results[selected] if display_results else {}
287
288
  source = sel.get('source', '')
288
289
  sys.stdout.write(f'\033[{height-1};1H\033[K Source: {source}'.ljust(width))
289
- sys.stdout.write(f'\033[{height};1H\033[K\033[44;37m j/k:Nav 1/2/3:Sort c:Concepts e:Explore p:Preview q:Quit [{selected+1}/{len(display_results)}] \033[0m')
290
+ sys.stdout.write(f'\033[{height};1H\033[K\033[7m j/k:Nav 1/2/3:Sort c:Concepts e:Explore p:Preview q:Quit [{selected+1}/{len(display_results)}] \033[0m')
290
291
 
291
292
  else: # preview mode
292
293
  sel = display_results[selected]
@@ -311,16 +312,26 @@ steps:
311
312
 
312
313
  sys.stdout.write(f'\033[{height-2};1H\033[K\033[90m{"─" * width}\033[0m')
313
314
  sys.stdout.write(f'\033[{height-1};1H\033[K [{preview_scroll+1}/{len(all_lines)} lines]')
314
- sys.stdout.write(f'\033[{height};1H\033[K\033[44;37m j/k:Scroll b:Back e:Explore q:Quit \033[0m')
315
+ sys.stdout.write(f'\033[{height};1H\033[K\033[7m j/k:Scroll b:Back e:Explore q:Quit \033[0m')
315
316
 
316
317
  sys.stdout.flush()
317
318
 
318
- c = sys.stdin.read(1)
319
+ c = os.read(fd, 1).decode('latin-1')
319
320
 
320
321
  if c == '\x1b':
321
- c2 = sys.stdin.read(1)
322
+ import select as _sel
323
+ if _sel.select([fd], [], [], 0.05)[0]:
324
+ c2 = os.read(fd, 1).decode('latin-1')
325
+ else:
326
+ if mode == 'preview':
327
+ mode = 'list'
328
+ sys.stdout.write('\033[2J\033[H')
329
+ else:
330
+ context['output'] = "Cancelled."
331
+ break
332
+ continue
322
333
  if c2 == '[':
323
- c3 = sys.stdin.read(1)
334
+ c3 = os.read(fd, 1).decode('latin-1')
324
335
  if c3 == 'A': # Up
325
336
  if mode == 'list' and selected > 0:
326
337
  selected -= 1
@@ -1,5 +1,6 @@
1
1
  jinx_name: web_search
2
2
  description: Search the web with interactive TUI
3
+ interactive: true
3
4
  inputs:
4
5
  - query: ""
5
6
  - provider: ""
@@ -54,18 +55,50 @@ steps:
54
55
  try:
55
56
  raw_results = search_web(query, provider=provider, num_results=num_results)
56
57
 
57
- # Normalize results
58
+ # Normalize results - handle different provider formats
58
59
  results = []
59
60
  if raw_results:
60
- for res in raw_results:
61
- if isinstance(res, dict):
61
+ # Perplexity returns [answer_text, [url1, url2, ...]]
62
+ if (len(raw_results) == 2
63
+ and isinstance(raw_results[0], str)
64
+ and isinstance(raw_results[1], (list, tuple))):
65
+ answer_text = raw_results[0]
66
+ citations = raw_results[1]
67
+ # First result: the AI answer
68
+ results.append({
69
+ 'title': 'AI Answer',
70
+ 'url': '',
71
+ 'snippet': answer_text[:500]
72
+ })
73
+ # Then each citation as a result
74
+ for url in citations:
75
+ url_str = str(url).strip()
76
+ try:
77
+ from urllib.parse import urlparse
78
+ domain = urlparse(url_str).netloc
79
+ except:
80
+ domain = url_str[:40]
62
81
  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', ''))
82
+ 'title': domain or url_str[:60],
83
+ 'url': url_str,
84
+ 'snippet': ''
66
85
  })
67
- else:
68
- results.append({'title': str(res)[:50], 'url': str(res), 'snippet': ''})
86
+ else:
87
+ for res in raw_results:
88
+ if isinstance(res, dict):
89
+ results.append({
90
+ 'title': res.get('title', 'No title'),
91
+ 'url': res.get('url', res.get('link', '')),
92
+ 'snippet': res.get('snippet', res.get('description', res.get('content', '')))
93
+ })
94
+ elif isinstance(res, str):
95
+ # Could be a URL or text
96
+ if res.startswith('http'):
97
+ results.append({'title': res[:60], 'url': res, 'snippet': ''})
98
+ else:
99
+ results.append({'title': res[:60], 'url': '', 'snippet': res[:200]})
100
+ else:
101
+ results.append({'title': str(res)[:60], 'url': str(res), 'snippet': ''})
69
102
 
70
103
  if not results:
71
104
  context['output'] = "No web results found."
@@ -128,7 +161,7 @@ steps:
128
161
  header = f" WEB SEARCH ({len(results)} results): '{query}' "
129
162
  else:
130
163
  header = f" PREVIEW: {results[selected]['title'][:width-12]} "
131
- sys.stdout.write(f'\033[44;37;1m{header.ljust(width)}\033[0m\n')
164
+ sys.stdout.write(f'\033[7;1m{header.ljust(width)}\033[0m\n')
132
165
 
133
166
  # Column headers
134
167
  if mode == 'list':
@@ -153,7 +186,7 @@ steps:
153
186
  line = line[:width-1]
154
187
 
155
188
  if idx == selected:
156
- sys.stdout.write(f'\033[47;30;1m>{line}\033[0m')
189
+ sys.stdout.write(f'\033[7;1m>{line}\033[0m')
157
190
  else:
158
191
  sys.stdout.write(f' {line}')
159
192
 
@@ -162,7 +195,7 @@ steps:
162
195
  sel = results[selected] if results else {}
163
196
  snippet = (sel.get('snippet') or '')[:width-2]
164
197
  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')
198
+ sys.stdout.write(f'\033[{height};1H\033[K\033[7m j/k:Nav p:Preview o:Open i:Incog c:Copy q:Quit [{selected+1}/{len(results)}] \033[0m')
166
199
 
167
200
  else: # preview mode
168
201
  for i in range(list_height):
@@ -173,16 +206,21 @@ steps:
173
206
 
174
207
  sys.stdout.write(f'\033[{height-2};1H\033[K\033[90m{"─" * width}\033[0m')
175
208
  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')
209
+ sys.stdout.write(f'\033[{height};1H\033[K\033[7m j/k:Scroll b:Back o:Open c:Copy q:Quit \033[0m')
177
210
 
178
211
  sys.stdout.flush()
179
212
 
180
- c = sys.stdin.read(1)
213
+ c = os.read(fd, 1).decode('latin-1')
181
214
 
182
215
  if c == '\x1b':
183
- c2 = sys.stdin.read(1)
216
+ import select as _sel
217
+ if _sel.select([fd], [], [], 0.05)[0]:
218
+ c2 = os.read(fd, 1).decode('latin-1')
219
+ else:
220
+ context['output'] = "Cancelled."
221
+ break
184
222
  if c2 == '[':
185
- c3 = sys.stdin.read(1)
223
+ c3 = os.read(fd, 1).decode('latin-1')
186
224
  if c3 == 'A':
187
225
  if mode == 'list' and selected > 0:
188
226
  selected -= 1
@@ -23,7 +23,7 @@ steps:
23
23
  if not model:
24
24
  model = npc.model if npc and npc.model else ""
25
25
  if not provider:
26
- provider = npc.provider if npc and npc.provider else "anthropic"
26
+ provider = npc.provider if npc and npc.provider else ""
27
27
 
28
28
  try:
29
29
  from npcsh.benchmark import BenchmarkRunner, run_benchmark
@@ -56,7 +56,7 @@ steps:
56
56
  output += "\n### Usage:\n"
57
57
  output += "```\n"
58
58
  output += "/benchmark action=quick\n"
59
- output += "/benchmark action=run model=gpt-4o provider=openai\n"
59
+ output += "/benchmark action=run model=<model> provider=<provider>\n"
60
60
  output += "/benchmark action=list\n"
61
61
  output += "```\n"
62
62