npcsh 1.1.20__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 (166) hide show
  1. npcsh/_state.py +5 -71
  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 +17 -6
  5. npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +17 -6
  6. npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +19 -8
  7. npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +52 -14
  8. npcsh/npc_team/jinxs/{bin → lib/utils}/benchmark.jinx +2 -2
  9. npcsh/npc_team/jinxs/{bin → lib/utils}/jinxs.jinx +12 -12
  10. npcsh/npc_team/jinxs/{bin → lib/utils}/models.jinx +7 -7
  11. npcsh/npc_team/jinxs/{bin → lib/utils}/setup.jinx +6 -6
  12. npcsh/npc_team/jinxs/modes/alicanto.jinx +1573 -296
  13. npcsh/npc_team/jinxs/modes/arxiv.jinx +5 -5
  14. npcsh/npc_team/jinxs/modes/config_tui.jinx +300 -0
  15. npcsh/npc_team/jinxs/modes/corca.jinx +3 -3
  16. npcsh/npc_team/jinxs/modes/git.jinx +795 -0
  17. {npcsh-1.1.20.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/modes}/kg.jinx +13 -13
  18. npcsh/npc_team/jinxs/modes/memories.jinx +414 -0
  19. npcsh/npc_team/jinxs/{bin → modes}/nql.jinx +10 -21
  20. npcsh/npc_team/jinxs/modes/papers.jinx +578 -0
  21. npcsh/npc_team/jinxs/modes/plonk.jinx +490 -304
  22. npcsh/npc_team/jinxs/modes/reattach.jinx +3 -3
  23. npcsh/npc_team/jinxs/modes/spool.jinx +3 -3
  24. npcsh/npc_team/jinxs/{bin → modes}/team.jinx +12 -12
  25. npcsh/npc_team/jinxs/modes/vixynt.jinx +388 -0
  26. npcsh/npc_team/jinxs/modes/wander.jinx +454 -181
  27. npcsh/npc_team/jinxs/modes/yap.jinx +10 -3
  28. npcsh/npcsh.py +112 -47
  29. npcsh/routes.py +4 -1
  30. npcsh/salmon_simulation.py +0 -0
  31. npcsh-1.1.21.data/data/npcsh/npc_team/alicanto.jinx +1633 -0
  32. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/arxiv.jinx +5 -5
  33. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/benchmark.jinx +2 -2
  34. npcsh-1.1.21.data/data/npcsh/npc_team/compress.jinx +428 -0
  35. npcsh-1.1.21.data/data/npcsh/npc_team/config_tui.jinx +300 -0
  36. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.jinx +3 -3
  37. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/db_search.jinx +17 -6
  38. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/file_search.jinx +17 -6
  39. npcsh-1.1.21.data/data/npcsh/npc_team/git.jinx +795 -0
  40. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/jinxs.jinx +12 -12
  41. {npcsh/npc_team/jinxs/bin → npcsh-1.1.21.data/data/npcsh/npc_team}/kg.jinx +13 -13
  42. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kg_search.jinx +19 -8
  43. npcsh-1.1.21.data/data/npcsh/npc_team/memories.jinx +414 -0
  44. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/models.jinx +7 -7
  45. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/nql.jinx +10 -21
  46. npcsh-1.1.21.data/data/npcsh/npc_team/papers.jinx +578 -0
  47. npcsh-1.1.21.data/data/npcsh/npc_team/plonk.jinx +565 -0
  48. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/reattach.jinx +3 -3
  49. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/setup.jinx +6 -6
  50. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/spool.jinx +3 -3
  51. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/team.jinx +12 -12
  52. npcsh-1.1.21.data/data/npcsh/npc_team/vixynt.jinx +388 -0
  53. npcsh-1.1.21.data/data/npcsh/npc_team/wander.jinx +728 -0
  54. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/web_search.jinx +52 -14
  55. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/yap.jinx +10 -3
  56. {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/METADATA +2 -2
  57. {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/RECORD +145 -150
  58. npcsh-1.1.21.dist-info/entry_points.txt +11 -0
  59. npcsh/npc_team/jinxs/bin/config_tui.jinx +0 -300
  60. npcsh/npc_team/jinxs/bin/memories.jinx +0 -317
  61. npcsh/npc_team/jinxs/bin/vixynt.jinx +0 -122
  62. npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +0 -73
  63. npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +0 -388
  64. npcsh/npc_team/jinxs/lib/research/paper_search.jinx +0 -412
  65. npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +0 -386
  66. npcsh/npc_team/plonkjr.npc +0 -23
  67. npcsh-1.1.20.data/data/npcsh/npc_team/alicanto.jinx +0 -356
  68. npcsh-1.1.20.data/data/npcsh/npc_team/compress.jinx +0 -140
  69. npcsh-1.1.20.data/data/npcsh/npc_team/config_tui.jinx +0 -300
  70. npcsh-1.1.20.data/data/npcsh/npc_team/mem_review.jinx +0 -73
  71. npcsh-1.1.20.data/data/npcsh/npc_team/mem_search.jinx +0 -388
  72. npcsh-1.1.20.data/data/npcsh/npc_team/memories.jinx +0 -317
  73. npcsh-1.1.20.data/data/npcsh/npc_team/paper_search.jinx +0 -412
  74. npcsh-1.1.20.data/data/npcsh/npc_team/plonk.jinx +0 -379
  75. npcsh-1.1.20.data/data/npcsh/npc_team/plonkjr.npc +0 -23
  76. npcsh-1.1.20.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -386
  77. npcsh-1.1.20.data/data/npcsh/npc_team/vixynt.jinx +0 -122
  78. npcsh-1.1.20.data/data/npcsh/npc_team/wander.jinx +0 -455
  79. npcsh-1.1.20.dist-info/entry_points.txt +0 -25
  80. /npcsh/npc_team/jinxs/lib/{orchestration → core}/convene.jinx +0 -0
  81. /npcsh/npc_team/jinxs/lib/{orchestration → core}/delegate.jinx +0 -0
  82. /npcsh/npc_team/jinxs/{bin → lib/core}/sample.jinx +0 -0
  83. /npcsh/npc_team/jinxs/{bin → lib/utils}/sync.jinx +0 -0
  84. /npcsh/npc_team/jinxs/{bin → modes}/roll.jinx +0 -0
  85. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/add_tab.jinx +0 -0
  86. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/alicanto.npc +0 -0
  87. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/alicanto.png +0 -0
  88. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
  89. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
  90. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/build.jinx +0 -0
  91. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/chat.jinx +0 -0
  92. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/click.jinx +0 -0
  93. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  94. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_pane.jinx +0 -0
  95. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_tab.jinx +0 -0
  96. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/cmd.jinx +0 -0
  97. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/compile.jinx +0 -0
  98. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/confirm.jinx +0 -0
  99. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/convene.jinx +0 -0
  100. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.npc +0 -0
  101. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.png +0 -0
  102. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca_example.png +0 -0
  103. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/delegate.jinx +0 -0
  104. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/edit_file.jinx +0 -0
  105. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/focus_pane.jinx +0 -0
  106. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/frederic.npc +0 -0
  107. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/frederic4.png +0 -0
  108. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.jinx +0 -0
  109. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.npc +0 -0
  110. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.png +0 -0
  111. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/help.jinx +0 -0
  112. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/incognide.jinx +0 -0
  113. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/init.jinx +0 -0
  114. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
  115. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  116. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/key_press.jinx +0 -0
  117. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
  118. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/list_panes.jinx +0 -0
  119. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  120. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/navigate.jinx +0 -0
  121. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/notify.jinx +0 -0
  122. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  123. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  124. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
  125. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/open_pane.jinx +0 -0
  126. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/ots.jinx +0 -0
  127. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/paste.jinx +0 -0
  128. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonk.npc +0 -0
  129. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonk.png +0 -0
  130. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  131. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/pti.jinx +0 -0
  132. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/python.jinx +0 -0
  133. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/read_pane.jinx +0 -0
  134. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/roll.jinx +0 -0
  135. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/run_terminal.jinx +0 -0
  136. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sample.jinx +0 -0
  137. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
  138. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/search.jinx +0 -0
  139. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/send_message.jinx +0 -0
  140. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/serve.jinx +0 -0
  141. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/set.jinx +0 -0
  142. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sh.jinx +0 -0
  143. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/shh.jinx +0 -0
  144. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sibiji.npc +0 -0
  145. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sibiji.png +0 -0
  146. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sleep.jinx +0 -0
  147. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/split_pane.jinx +0 -0
  148. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/spool.png +0 -0
  149. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sql.jinx +0 -0
  150. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch.jinx +0 -0
  151. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch_npc.jinx +0 -0
  152. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch_tab.jinx +0 -0
  153. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switches.jinx +0 -0
  154. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sync.jinx +0 -0
  155. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
  156. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/trigger.jinx +0 -0
  157. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/type_text.jinx +0 -0
  158. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/usage.jinx +0 -0
  159. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  160. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/wait.jinx +0 -0
  161. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/write_file.jinx +0 -0
  162. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/yap.png +0 -0
  163. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/zen_mode.jinx +0 -0
  164. {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/WHEEL +0 -0
  165. {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/licenses/LICENSE +0 -0
  166. {npcsh-1.1.20.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':
@@ -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':
@@ -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':
@@ -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':
@@ -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
 
@@ -104,8 +104,8 @@ steps:
104
104
 
105
105
  # Search filter
106
106
  if ui.search_query:
107
- q = ui.search_query.lower()
108
- items = [j for j in items if q in j['name'].lower() or q in j['description'].lower()]
107
+ srch = ui.search_query.lower()
108
+ items = [j for j in items if srch in j['name'].lower() or srch in j['description'].lower()]
109
109
 
110
110
  ui.filtered = items
111
111
  # Clamp selection
@@ -129,8 +129,8 @@ steps:
129
129
  # ── header ──
130
130
  hdr = " Jinxs "
131
131
  pad = '=' * W
132
- out.append(wline(1, f"\033[44;37;1m{pad}\033[0m"))
133
- out.append(f"\033[1;{max(1, (W - len(hdr)) // 2)}H\033[44;37;1m{hdr}\033[0m")
132
+ out.append(wline(1, f"\033[7;1m{pad}\033[0m"))
133
+ out.append(f"\033[1;{max(1, (W - len(hdr)) // 2)}H\033[7;1m{hdr}\033[0m")
134
134
 
135
135
  # ── tabs ──
136
136
  tb = ""
@@ -184,7 +184,7 @@ steps:
184
184
  foot = " [j/k] Scroll [q/Esc] Back"
185
185
  else:
186
186
  foot = " [Tab] Filter [j/k] Nav [Enter] Detail [/] Search [q] Quit"
187
- out.append(wline(H, f"\033[44;37m{foot[:W].ljust(W)}\033[0m"))
187
+ out.append(wline(H, f"\033[7m{foot[:W].ljust(W)}\033[0m"))
188
188
 
189
189
  sys.stdout.write(''.join(out))
190
190
  sys.stdout.flush()
@@ -296,10 +296,10 @@ steps:
296
296
  return True
297
297
 
298
298
  def handle_esc():
299
- if select.select([sys.stdin], [], [], 0.05)[0]:
300
- c2 = sys.stdin.read(1)
299
+ if select.select([fd], [], [], 0.05)[0]:
300
+ c2 = os.read(fd, 1).decode('latin-1')
301
301
  if c2 == '[':
302
- c3 = sys.stdin.read(1)
302
+ c3 = os.read(fd, 1).decode('latin-1')
303
303
  if c3 == 'A':
304
304
  nav_up()
305
305
  elif c3 == 'B':
@@ -322,10 +322,10 @@ steps:
322
322
  def handle_search(c):
323
323
  if c == '\x1b':
324
324
  # Check if arrow key or bare esc
325
- if select.select([sys.stdin], [], [], 0.05)[0]:
326
- c2 = sys.stdin.read(1)
325
+ if select.select([fd], [], [], 0.05)[0]:
326
+ c2 = os.read(fd, 1).decode('latin-1')
327
327
  if c2 == '[':
328
- sys.stdin.read(1) # consume arrow char
328
+ os.read(fd, 1).decode('latin-1') # consume arrow char
329
329
  else:
330
330
  ui.search_mode = False
331
331
  ui.search_buf = ""
@@ -395,7 +395,7 @@ steps:
395
395
  sys.stdout.flush()
396
396
  render()
397
397
  while True:
398
- c = sys.stdin.read(1)
398
+ c = os.read(fd, 1).decode('latin-1')
399
399
  if not handle(c):
400
400
  break
401
401
  render()
@@ -120,8 +120,8 @@ steps:
120
120
  # -- header --
121
121
  hdr = " Models "
122
122
  pad = '=' * W
123
- out.append(wline(1, f"\033[44;37;1m{pad}\033[0m"))
124
- out.append(f"\033[1;{max(1,(W - len(hdr)) // 2)}H\033[44;37;1m{hdr}\033[0m")
123
+ out.append(wline(1, f"\033[7;1m{pad}\033[0m"))
124
+ out.append(f"\033[1;{max(1,(W - len(hdr)) // 2)}H\033[7;1m{hdr}\033[0m")
125
125
 
126
126
  # -- tabs --
127
127
  tb = ""
@@ -205,7 +205,7 @@ steps:
205
205
  # combine status into footer area
206
206
  foot = " [Tab] Filter [j/k] Nav [c] Set Chat [v] Set Vision [e] Set Embed [r] Set Reasoning [d] Refresh [q] Quit"
207
207
  out.append(wline(H - 1, stat_line))
208
- out.append(wline(H, f"\033[44;37m{foot[:W].ljust(W)}\033[0m"))
208
+ out.append(wline(H, f"\033[7m{foot[:W].ljust(W)}\033[0m"))
209
209
 
210
210
  sys.stdout.write(''.join(out))
211
211
  sys.stdout.flush()
@@ -240,10 +240,10 @@ steps:
240
240
  return True
241
241
 
242
242
  def handle_esc():
243
- if select.select([sys.stdin], [], [], 0.05)[0]:
244
- c2 = sys.stdin.read(1)
243
+ if select.select([fd], [], [], 0.05)[0]:
244
+ c2 = os.read(fd, 1).decode('latin-1')
245
245
  if c2 == '[':
246
- c3 = sys.stdin.read(1)
246
+ c3 = os.read(fd, 1).decode('latin-1')
247
247
  if c3 == 'A':
248
248
  nav_up()
249
249
  elif c3 == 'B':
@@ -331,7 +331,7 @@ steps:
331
331
  sys.stdout.flush()
332
332
  render()
333
333
  while True:
334
- c = sys.stdin.read(1)
334
+ c = os.read(fd, 1).decode('latin-1')
335
335
  if not handle(c):
336
336
  break
337
337
  render()
@@ -103,8 +103,8 @@ steps:
103
103
  out = []
104
104
  out.append("\033[2J\033[H")
105
105
  header = " NPCSH Setup Wizard "
106
- out.append(f"\033[1;1H\033[44;37;1m{'=' * width}\033[0m")
107
- out.append(f"\033[1;{(width - len(header)) // 2}H\033[44;37;1m{header}\033[0m")
106
+ out.append(f"\033[1;1H\033[7;1m{'=' * width}\033[0m")
107
+ out.append(f"\033[1;{(width - len(header)) // 2}H\033[7;1m{header}\033[0m")
108
108
 
109
109
  if state.phase == 'detect':
110
110
  out.append(f"\033[3;2H\033[1mDetected Providers:\033[0m")
@@ -149,10 +149,10 @@ steps:
149
149
  if c == 'q':
150
150
  return False
151
151
  if c == '\x1b':
152
- if select.select([sys.stdin], [], [], 0.05)[0]:
153
- c2 = sys.stdin.read(1)
152
+ if select.select([fd], [], [], 0.05)[0]:
153
+ c2 = os.read(fd, 1).decode('latin-1')
154
154
  if c2 == '[':
155
- c3 = sys.stdin.read(1)
155
+ c3 = os.read(fd, 1).decode('latin-1')
156
156
  if c3 == 'A' and state.phase == 'select_chat':
157
157
  state.selected_idx = max(0, state.selected_idx - 1)
158
158
  if state.selected_idx < state.scroll_offset:
@@ -225,7 +225,7 @@ steps:
225
225
  run_detection()
226
226
  render_screen()
227
227
  while True:
228
- c = sys.stdin.read(1)
228
+ c = os.read(fd, 1).decode('latin-1')
229
229
  if not handle_input(c):
230
230
  break
231
231
  render_screen()