npcsh 1.1.20__py3-none-any.whl → 1.1.22__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 (186) hide show
  1. npcsh/_state.py +15 -76
  2. npcsh/benchmark/npcsh_agent.py +22 -14
  3. npcsh/benchmark/templates/install-npcsh.sh.j2 +2 -2
  4. npcsh/diff_viewer.py +3 -3
  5. npcsh/mcp_server.py +9 -1
  6. npcsh/npc_team/alicanto.npc +12 -6
  7. npcsh/npc_team/corca.npc +0 -1
  8. npcsh/npc_team/frederic.npc +2 -3
  9. npcsh/npc_team/jinxs/lib/core/compress.jinx +373 -85
  10. npcsh/npc_team/jinxs/lib/core/edit_file.jinx +83 -61
  11. npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +17 -6
  12. npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +17 -6
  13. npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +52 -14
  14. npcsh/npc_team/jinxs/{bin → lib/utils}/benchmark.jinx +2 -2
  15. npcsh/npc_team/jinxs/{bin → lib/utils}/jinxs.jinx +12 -12
  16. npcsh/npc_team/jinxs/{bin → lib/utils}/models.jinx +7 -7
  17. npcsh/npc_team/jinxs/{bin → lib/utils}/setup.jinx +6 -6
  18. npcsh/npc_team/jinxs/modes/alicanto.jinx +1633 -295
  19. npcsh/npc_team/jinxs/modes/arxiv.jinx +5 -5
  20. npcsh/npc_team/jinxs/modes/build.jinx +378 -0
  21. npcsh/npc_team/jinxs/modes/config_tui.jinx +300 -0
  22. npcsh/npc_team/jinxs/modes/convene.jinx +597 -0
  23. npcsh/npc_team/jinxs/modes/corca.jinx +777 -387
  24. npcsh/npc_team/jinxs/modes/git.jinx +795 -0
  25. {npcsh-1.1.20.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/modes}/kg.jinx +82 -15
  26. npcsh/npc_team/jinxs/modes/memories.jinx +414 -0
  27. npcsh/npc_team/jinxs/{bin → modes}/nql.jinx +10 -21
  28. npcsh/npc_team/jinxs/modes/papers.jinx +578 -0
  29. npcsh/npc_team/jinxs/modes/plonk.jinx +503 -308
  30. npcsh/npc_team/jinxs/modes/reattach.jinx +3 -3
  31. npcsh/npc_team/jinxs/modes/spool.jinx +3 -3
  32. npcsh/npc_team/jinxs/{bin → modes}/team.jinx +12 -12
  33. npcsh/npc_team/jinxs/modes/vixynt.jinx +388 -0
  34. npcsh/npc_team/jinxs/modes/wander.jinx +454 -181
  35. npcsh/npc_team/jinxs/modes/yap.jinx +630 -182
  36. npcsh/npc_team/kadiefa.npc +2 -1
  37. npcsh/npc_team/sibiji.npc +3 -3
  38. npcsh/npcsh.py +112 -47
  39. npcsh/routes.py +4 -1
  40. npcsh/salmon_simulation.py +0 -0
  41. npcsh-1.1.22.data/data/npcsh/npc_team/alicanto.jinx +1694 -0
  42. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/alicanto.npc +12 -6
  43. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/arxiv.jinx +5 -5
  44. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/benchmark.jinx +2 -2
  45. npcsh-1.1.22.data/data/npcsh/npc_team/build.jinx +378 -0
  46. npcsh-1.1.22.data/data/npcsh/npc_team/compress.jinx +428 -0
  47. npcsh-1.1.22.data/data/npcsh/npc_team/config_tui.jinx +300 -0
  48. npcsh-1.1.22.data/data/npcsh/npc_team/corca.jinx +820 -0
  49. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca.npc +0 -1
  50. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/db_search.jinx +17 -6
  51. npcsh-1.1.22.data/data/npcsh/npc_team/edit_file.jinx +119 -0
  52. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/file_search.jinx +17 -6
  53. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/frederic.npc +2 -3
  54. npcsh-1.1.22.data/data/npcsh/npc_team/git.jinx +795 -0
  55. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/jinxs.jinx +12 -12
  56. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/kadiefa.npc +2 -1
  57. {npcsh/npc_team/jinxs/bin → npcsh-1.1.22.data/data/npcsh/npc_team}/kg.jinx +82 -15
  58. npcsh-1.1.22.data/data/npcsh/npc_team/memories.jinx +414 -0
  59. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/models.jinx +7 -7
  60. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/nql.jinx +10 -21
  61. npcsh-1.1.22.data/data/npcsh/npc_team/papers.jinx +578 -0
  62. npcsh-1.1.22.data/data/npcsh/npc_team/plonk.jinx +574 -0
  63. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/reattach.jinx +3 -3
  64. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/setup.jinx +6 -6
  65. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sibiji.npc +3 -3
  66. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/spool.jinx +3 -3
  67. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/team.jinx +12 -12
  68. npcsh-1.1.22.data/data/npcsh/npc_team/vixynt.jinx +388 -0
  69. npcsh-1.1.22.data/data/npcsh/npc_team/wander.jinx +728 -0
  70. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/web_search.jinx +52 -14
  71. npcsh-1.1.22.data/data/npcsh/npc_team/yap.jinx +716 -0
  72. {npcsh-1.1.20.dist-info → npcsh-1.1.22.dist-info}/METADATA +246 -281
  73. npcsh-1.1.22.dist-info/RECORD +240 -0
  74. npcsh-1.1.22.dist-info/entry_points.txt +11 -0
  75. npcsh/npc_team/jinxs/bin/config_tui.jinx +0 -300
  76. npcsh/npc_team/jinxs/bin/memories.jinx +0 -317
  77. npcsh/npc_team/jinxs/bin/vixynt.jinx +0 -122
  78. npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +0 -418
  79. npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +0 -73
  80. npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +0 -388
  81. npcsh/npc_team/jinxs/lib/core/search.jinx +0 -54
  82. npcsh/npc_team/jinxs/lib/research/paper_search.jinx +0 -412
  83. npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +0 -386
  84. npcsh/npc_team/jinxs/lib/utils/build.jinx +0 -65
  85. npcsh/npc_team/plonkjr.npc +0 -23
  86. npcsh-1.1.20.data/data/npcsh/npc_team/alicanto.jinx +0 -356
  87. npcsh-1.1.20.data/data/npcsh/npc_team/build.jinx +0 -65
  88. npcsh-1.1.20.data/data/npcsh/npc_team/compress.jinx +0 -140
  89. npcsh-1.1.20.data/data/npcsh/npc_team/config_tui.jinx +0 -300
  90. npcsh-1.1.20.data/data/npcsh/npc_team/corca.jinx +0 -430
  91. npcsh-1.1.20.data/data/npcsh/npc_team/edit_file.jinx +0 -97
  92. npcsh-1.1.20.data/data/npcsh/npc_team/kg_search.jinx +0 -418
  93. npcsh-1.1.20.data/data/npcsh/npc_team/mem_review.jinx +0 -73
  94. npcsh-1.1.20.data/data/npcsh/npc_team/mem_search.jinx +0 -388
  95. npcsh-1.1.20.data/data/npcsh/npc_team/memories.jinx +0 -317
  96. npcsh-1.1.20.data/data/npcsh/npc_team/paper_search.jinx +0 -412
  97. npcsh-1.1.20.data/data/npcsh/npc_team/plonk.jinx +0 -379
  98. npcsh-1.1.20.data/data/npcsh/npc_team/plonkjr.npc +0 -23
  99. npcsh-1.1.20.data/data/npcsh/npc_team/search.jinx +0 -54
  100. npcsh-1.1.20.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -386
  101. npcsh-1.1.20.data/data/npcsh/npc_team/vixynt.jinx +0 -122
  102. npcsh-1.1.20.data/data/npcsh/npc_team/wander.jinx +0 -455
  103. npcsh-1.1.20.data/data/npcsh/npc_team/yap.jinx +0 -268
  104. npcsh-1.1.20.dist-info/RECORD +0 -248
  105. npcsh-1.1.20.dist-info/entry_points.txt +0 -25
  106. /npcsh/npc_team/jinxs/lib/{orchestration → core}/convene.jinx +0 -0
  107. /npcsh/npc_team/jinxs/lib/{orchestration → core}/delegate.jinx +0 -0
  108. /npcsh/npc_team/jinxs/{bin → lib/core}/sample.jinx +0 -0
  109. /npcsh/npc_team/jinxs/lib/{core → utils}/chat.jinx +0 -0
  110. /npcsh/npc_team/jinxs/lib/{core → utils}/cmd.jinx +0 -0
  111. /npcsh/npc_team/jinxs/{bin → lib/utils}/sync.jinx +0 -0
  112. /npcsh/npc_team/jinxs/{bin → modes}/roll.jinx +0 -0
  113. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/add_tab.jinx +0 -0
  114. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/alicanto.png +0 -0
  115. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
  116. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
  117. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/chat.jinx +0 -0
  118. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/click.jinx +0 -0
  119. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  120. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_pane.jinx +0 -0
  121. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_tab.jinx +0 -0
  122. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/cmd.jinx +0 -0
  123. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/compile.jinx +0 -0
  124. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/confirm.jinx +0 -0
  125. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/convene.jinx +0 -0
  126. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca.png +0 -0
  127. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca_example.png +0 -0
  128. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/delegate.jinx +0 -0
  129. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/focus_pane.jinx +0 -0
  130. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/frederic4.png +0 -0
  131. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.jinx +0 -0
  132. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.npc +0 -0
  133. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.png +0 -0
  134. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/help.jinx +0 -0
  135. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/incognide.jinx +0 -0
  136. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/init.jinx +0 -0
  137. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  138. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/key_press.jinx +0 -0
  139. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
  140. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/list_panes.jinx +0 -0
  141. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  142. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/navigate.jinx +0 -0
  143. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/notify.jinx +0 -0
  144. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  145. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  146. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
  147. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/open_pane.jinx +0 -0
  148. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/ots.jinx +0 -0
  149. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/paste.jinx +0 -0
  150. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonk.npc +0 -0
  151. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonk.png +0 -0
  152. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  153. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/pti.jinx +0 -0
  154. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/python.jinx +0 -0
  155. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/read_pane.jinx +0 -0
  156. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/roll.jinx +0 -0
  157. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/run_terminal.jinx +0 -0
  158. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sample.jinx +0 -0
  159. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
  160. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/send_message.jinx +0 -0
  161. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/serve.jinx +0 -0
  162. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/set.jinx +0 -0
  163. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sh.jinx +0 -0
  164. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/shh.jinx +0 -0
  165. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sibiji.png +0 -0
  166. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sleep.jinx +0 -0
  167. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/split_pane.jinx +0 -0
  168. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/spool.png +0 -0
  169. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sql.jinx +0 -0
  170. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch.jinx +0 -0
  171. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch_npc.jinx +0 -0
  172. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch_tab.jinx +0 -0
  173. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switches.jinx +0 -0
  174. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sync.jinx +0 -0
  175. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
  176. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/trigger.jinx +0 -0
  177. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/type_text.jinx +0 -0
  178. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/usage.jinx +0 -0
  179. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  180. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/wait.jinx +0 -0
  181. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/write_file.jinx +0 -0
  182. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/yap.png +0 -0
  183. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/zen_mode.jinx +0 -0
  184. {npcsh-1.1.20.dist-info → npcsh-1.1.22.dist-info}/WHEEL +0 -0
  185. {npcsh-1.1.20.dist-info → npcsh-1.1.22.dist-info}/licenses/LICENSE +0 -0
  186. {npcsh-1.1.20.dist-info → npcsh-1.1.22.dist-info}/top_level.txt +0 -0
@@ -1,386 +0,0 @@
1
- jinx_name: semantic_scholar
2
- description: Search Semantic Scholar with interactive TUI. Requires S2_API_KEY env var.
3
- inputs:
4
- - query: ""
5
- - limit: "20"
6
- - text: "false"
7
-
8
- steps:
9
- - name: search_s2
10
- engine: python
11
- code: |
12
- import os
13
- import sys
14
- import tty
15
- import termios
16
- import time
17
- import requests
18
- import webbrowser
19
- import subprocess
20
-
21
- query = context.get('query', '')
22
- limit = int(context.get('limit', 20))
23
- text_mode = context.get('text', '').lower() in ('true', '1', 'yes')
24
-
25
- if not query:
26
- lines = [
27
- "Usage: /semantic_scholar <query>",
28
- "",
29
- "Options:",
30
- " limit - Max results (default 20)",
31
- " text - Text-only output, no TUI (true/false)",
32
- "",
33
- "TUI Controls:",
34
- " j/k or arrows - Navigate",
35
- " 1/2/3 - Sort by year/citations/author",
36
- " y - Filter by year",
37
- " p - Preview abstract/TL;DR",
38
- " o - Open in browser",
39
- " i - Open in incognide",
40
- " d - Download paper info",
41
- " q/ESC - Quit",
42
- "",
43
- "Examples:",
44
- " /semantic_scholar machine learning",
45
- " /semantic_scholar neural networks limit=50",
46
- ]
47
- context['output'] = "\n".join(lines)
48
- else:
49
- api_key = os.environ.get('S2_API_KEY')
50
- if not api_key:
51
- context['output'] = "Error: S2_API_KEY environment variable not set.\nGet one at https://www.semanticscholar.org/product/api"
52
- else:
53
- url = "https://api.semanticscholar.org/graph/v1/paper/search"
54
- headers = {"x-api-key": api_key}
55
- params = {
56
- "query": query,
57
- "limit": limit,
58
- "fields": "title,abstract,authors,year,citationCount,url,tldr,venue,openAccessPdf"
59
- }
60
-
61
- try:
62
- response = requests.get(url, headers=headers, params=params, timeout=30)
63
- response.raise_for_status()
64
- papers = response.json().get('data', [])
65
-
66
- if not papers:
67
- context['output'] = f"No papers found for: {query}"
68
- elif text_mode:
69
- # Text-only output
70
- results = []
71
- for i, paper in enumerate(papers, 1):
72
- title = paper.get('title', 'No title')
73
- year = paper.get('year', '?')
74
- citations = paper.get('citationCount', 0)
75
- authors = ', '.join([a.get('name', '') for a in paper.get('authors', [])[:3]])
76
- if len(paper.get('authors', [])) > 3:
77
- authors += ' et al.'
78
- abstract = paper.get('abstract', '')[:200] + '...' if paper.get('abstract') else 'No abstract'
79
- tldr = paper.get('tldr', {}).get('text', '') if paper.get('tldr') else ''
80
- paper_url = paper.get('url', '')
81
-
82
- results.append(f"{i}. {title} ({year})")
83
- results.append(f" Authors: {authors}")
84
- results.append(f" Citations: {citations}")
85
- if tldr:
86
- results.append(f" TL;DR: {tldr}")
87
- else:
88
- results.append(f" Abstract: {abstract}")
89
- results.append(f" URL: {paper_url}")
90
- results.append("")
91
-
92
- context['output'] = f"Found {len(papers)} papers:\n\n" + "\n".join(results)
93
- context['papers'] = papers
94
- else:
95
- # Interactive TUI mode
96
- def get_terminal_size():
97
- try:
98
- size = os.get_terminal_size()
99
- return size.columns, size.lines
100
- except:
101
- return 80, 24
102
-
103
- width, height = get_terminal_size()
104
- selected = 0
105
- scroll = 0
106
- list_height = height - 5
107
- mode = 'list'
108
- preview_scroll = 0
109
- sort_mode = 'relevance' # relevance, year, citations, author
110
- year_filter = None
111
-
112
- def sort_papers(papers, sort_mode):
113
- if sort_mode == 'year':
114
- return sorted(papers, key=lambda x: x.get('year') or 0, reverse=True)
115
- elif sort_mode == 'citations':
116
- return sorted(papers, key=lambda x: x.get('citationCount') or 0, reverse=True)
117
- elif sort_mode == 'author':
118
- return sorted(papers, key=lambda x: (x.get('authors', [{}])[0].get('name', '') if x.get('authors') else ''))
119
- return papers # relevance = original order
120
-
121
- def filter_papers(papers, year_filter):
122
- if not year_filter:
123
- return papers
124
- return [p for p in papers if p.get('year') and p.get('year') >= year_filter]
125
-
126
- display_papers = filter_papers(sort_papers(papers, sort_mode), year_filter)
127
-
128
- fd = sys.stdin.fileno()
129
- old_settings = termios.tcgetattr(fd)
130
-
131
- try:
132
- tty.setcbreak(fd)
133
- sys.stdout.write('\033[?25l')
134
- sys.stdout.write('\033[2J\033[H')
135
-
136
- while True:
137
- width, height = get_terminal_size()
138
- list_height = height - 5
139
-
140
- if mode == 'list':
141
- if selected < scroll:
142
- scroll = selected
143
- elif selected >= scroll + list_height:
144
- scroll = selected - list_height + 1
145
-
146
- sys.stdout.write('\033[H')
147
-
148
- # Header
149
- if mode == 'list':
150
- sort_ind = {'relevance': '0', 'year': '1', 'citations': '2', 'author': '3'}[sort_mode]
151
- yr = f" year>={year_filter}" if year_filter else ""
152
- header = f" SEMANTIC SCHOLAR ({len(display_papers)} papers): '{query}' [sort:{sort_mode}({sort_ind}){yr}] "
153
- else:
154
- header = f" PREVIEW: {display_papers[selected].get('title', '')[:width-12]} "
155
- sys.stdout.write(f'\033[44;37;1m{header.ljust(width)}\033[0m\n')
156
-
157
- # Column headers
158
- if mode == 'list':
159
- col_header = f' {"YEAR":<6} {"CITE":<6} {"AUTHORS":<25} {"TITLE":<50}'
160
- sys.stdout.write(f'\033[90m{col_header[:width]}\033[0m\n')
161
- else:
162
- sys.stdout.write(f'\033[90m{"─" * width}\033[0m\n')
163
-
164
- if mode == 'list':
165
- for i in range(list_height):
166
- idx = scroll + i
167
- sys.stdout.write(f'\033[{3+i};1H\033[K')
168
- if idx >= len(display_papers):
169
- continue
170
-
171
- p = display_papers[idx]
172
- year = str(p.get('year') or '?')[:6]
173
- citations = str(p.get('citationCount') or 0)[:6]
174
- authors = ', '.join([a.get('name', '')[:15] for a in p.get('authors', [])[:2]])
175
- if len(p.get('authors', [])) > 2:
176
- authors += ' et al.'
177
- authors = authors[:25]
178
- title = (p.get('title') or '')[:60].replace('\n', ' ')
179
-
180
- line = f" {year:<6} {citations:<6} {authors:<25} {title}"
181
- line = line[:width-1]
182
-
183
- if idx == selected:
184
- sys.stdout.write(f'\033[7;1m>{line}\033[0m')
185
- else:
186
- sys.stdout.write(f' {line}')
187
-
188
- # Status bar
189
- sys.stdout.write(f'\033[{height-2};1H\033[K\033[90m{"─" * width}\033[0m')
190
- sel = display_papers[selected] if display_papers else {}
191
- venue = (sel.get('venue') or '')[:30]
192
- has_pdf = 'PDF' if sel.get('openAccessPdf') else ''
193
- sys.stdout.write(f'\033[{height-1};1H\033[K {venue} {has_pdf}'.ljust(width))
194
- sys.stdout.write(f'\033[{height};1H\033[K\033[44;37m j/k:Nav 0/1/2/3:Sort y:Year p:Preview o:Open i:Incog d:Download q:Quit [{selected+1}/{len(display_papers)}] \033[0m')
195
-
196
- else: # preview mode
197
- sel = display_papers[selected]
198
- tldr = sel.get('tldr', {}).get('text', '') if sel.get('tldr') else ''
199
- abstract = sel.get('abstract') or 'No abstract available'
200
-
201
- # Build preview lines
202
- preview_lines = [
203
- f"Title: {sel.get('title', '')}",
204
- "",
205
- f"Year: {sel.get('year', '?')}",
206
- f"Citations: {sel.get('citationCount', 0)}",
207
- f"Venue: {sel.get('venue', '')}",
208
- "",
209
- "Authors:",
210
- ]
211
- for a in sel.get('authors', []):
212
- preview_lines.append(f" - {a.get('name', '')}")
213
- preview_lines.append("")
214
-
215
- if tldr:
216
- preview_lines.append("TL;DR:")
217
- # Word wrap TL;DR
218
- words = tldr.split()
219
- line = ""
220
- for w in words:
221
- if len(line) + len(w) + 1 > width - 4:
222
- preview_lines.append(f" {line}")
223
- line = w
224
- else:
225
- line = f"{line} {w}" if line else w
226
- if line:
227
- preview_lines.append(f" {line}")
228
- preview_lines.append("")
229
-
230
- preview_lines.append("Abstract:")
231
- # Word wrap abstract
232
- words = abstract.split()
233
- line = ""
234
- for w in words:
235
- if len(line) + len(w) + 1 > width - 4:
236
- preview_lines.append(f" {line}")
237
- line = w
238
- else:
239
- line = f"{line} {w}" if line else w
240
- if line:
241
- preview_lines.append(f" {line}")
242
-
243
- preview_lines.append("")
244
- preview_lines.append(f"URL: {sel.get('url', '')}")
245
- if sel.get('openAccessPdf'):
246
- preview_lines.append(f"PDF: {sel.get('openAccessPdf', {}).get('url', '')}")
247
-
248
- for i in range(list_height):
249
- idx = preview_scroll + i
250
- sys.stdout.write(f'\033[{3+i};1H\033[K')
251
- if idx < len(preview_lines):
252
- sys.stdout.write(preview_lines[idx][:width-1])
253
-
254
- sys.stdout.write(f'\033[{height-2};1H\033[K\033[90m{"─" * width}\033[0m')
255
- sys.stdout.write(f'\033[{height-1};1H\033[K [{preview_scroll+1}/{len(preview_lines)} lines]')
256
- sys.stdout.write(f'\033[{height};1H\033[K\033[44;37m j/k:Scroll b:Back o:Open d:Download q:Quit \033[0m')
257
-
258
- sys.stdout.flush()
259
-
260
- c = sys.stdin.read(1)
261
-
262
- if c == '\x1b':
263
- c2 = sys.stdin.read(1)
264
- if c2 == '[':
265
- c3 = sys.stdin.read(1)
266
- if c3 == 'A': # Up
267
- if mode == 'list' and selected > 0:
268
- selected -= 1
269
- elif mode == 'preview' and preview_scroll > 0:
270
- preview_scroll -= 1
271
- elif c3 == 'B': # Down
272
- if mode == 'list' and selected < len(display_papers) - 1:
273
- selected += 1
274
- elif mode == 'preview' and preview_scroll < 100:
275
- preview_scroll += 1
276
- else:
277
- if mode == 'preview':
278
- mode = 'list'
279
- sys.stdout.write('\033[2J\033[H')
280
- else:
281
- context['output'] = "Cancelled."
282
- break
283
- continue
284
-
285
- if c == 'q' or c == '\x03':
286
- context['output'] = "Cancelled."
287
- break
288
- elif c == 'k':
289
- if mode == 'list' and selected > 0:
290
- selected -= 1
291
- elif mode == 'preview' and preview_scroll > 0:
292
- preview_scroll -= 1
293
- elif c == 'j':
294
- if mode == 'list' and selected < len(display_papers) - 1:
295
- selected += 1
296
- elif mode == 'preview' and preview_scroll < 100:
297
- preview_scroll += 1
298
- elif c == '0':
299
- sort_mode = 'relevance'
300
- display_papers = filter_papers(sort_papers(papers, sort_mode), year_filter)
301
- selected = 0
302
- scroll = 0
303
- elif c == '1':
304
- sort_mode = 'year'
305
- display_papers = filter_papers(sort_papers(papers, sort_mode), year_filter)
306
- selected = 0
307
- scroll = 0
308
- elif c == '2':
309
- sort_mode = 'citations'
310
- display_papers = filter_papers(sort_papers(papers, sort_mode), year_filter)
311
- selected = 0
312
- scroll = 0
313
- elif c == '3':
314
- sort_mode = 'author'
315
- display_papers = filter_papers(sort_papers(papers, sort_mode), year_filter)
316
- selected = 0
317
- scroll = 0
318
- elif c == 'y' and mode == 'list':
319
- # Cycle through year filters
320
- import datetime
321
- current_year = datetime.datetime.now().year
322
- if year_filter is None:
323
- year_filter = current_year - 1
324
- elif year_filter == current_year - 1:
325
- year_filter = current_year - 3
326
- elif year_filter == current_year - 3:
327
- year_filter = current_year - 5
328
- else:
329
- year_filter = None
330
- display_papers = filter_papers(sort_papers(papers, sort_mode), year_filter)
331
- selected = 0
332
- scroll = 0
333
- elif c == 'p' and mode == 'list' and display_papers:
334
- mode = 'preview'
335
- preview_scroll = 0
336
- sys.stdout.write('\033[2J\033[H')
337
- elif c == 'b' and mode == 'preview':
338
- mode = 'list'
339
- sys.stdout.write('\033[2J\033[H')
340
- elif c == 'o' and display_papers:
341
- sel = display_papers[selected]
342
- paper_url = sel.get('url', '')
343
- if paper_url:
344
- webbrowser.open(paper_url)
345
- elif c == 'i' and display_papers:
346
- sel = display_papers[selected]
347
- paper_url = sel.get('url', '')
348
- if paper_url:
349
- try:
350
- subprocess.run(['npcsh', '-c', f'/navigate url={paper_url}'], check=False, capture_output=True)
351
- except:
352
- webbrowser.open(paper_url)
353
- elif c == 'd' and display_papers:
354
- sel = display_papers[selected]
355
- # Save paper info to file
356
- title = sel.get('title', 'paper')
357
- safe_title = "".join(c if c.isalnum() or c in ' -_' else '_' for c in title)[:50]
358
- filename = f"{safe_title}.txt"
359
- with open(filename, 'w') as f:
360
- f.write(f"Title: {sel.get('title', '')}\n")
361
- f.write(f"Year: {sel.get('year', '')}\n")
362
- f.write(f"Citations: {sel.get('citationCount', 0)}\n")
363
- f.write(f"Venue: {sel.get('venue', '')}\n")
364
- f.write(f"Authors: {', '.join([a.get('name', '') for a in sel.get('authors', [])])}\n")
365
- f.write(f"URL: {sel.get('url', '')}\n")
366
- if sel.get('openAccessPdf'):
367
- f.write(f"PDF: {sel.get('openAccessPdf', {}).get('url', '')}\n")
368
- f.write(f"\nAbstract:\n{sel.get('abstract', '')}\n")
369
- if sel.get('tldr'):
370
- f.write(f"\nTL;DR:\n{sel.get('tldr', {}).get('text', '')}\n")
371
- context['output'] = f"Saved to: {filename}"
372
- break
373
- elif c in ('\r', '\n') and display_papers:
374
- sel = display_papers[selected]
375
- context['output'] = f"Selected: {sel.get('title', '')}\nURL: {sel.get('url', '')}"
376
- context['selected_paper'] = sel
377
- break
378
-
379
- finally:
380
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
381
- sys.stdout.write('\033[?25h')
382
- sys.stdout.write('\033[2J\033[H')
383
- sys.stdout.flush()
384
-
385
- except requests.exceptions.RequestException as e:
386
- context['output'] = f"Semantic Scholar API error: {e}"
@@ -1,65 +0,0 @@
1
- jinx_name: "build"
2
- description: "Build deployment artifacts for NPC team"
3
- inputs:
4
- - target: "flask"
5
- - outdir: "./build"
6
- - team: "./npc_team"
7
- - port: 5337
8
- - cors: ""
9
- steps:
10
- - name: "execute_build"
11
- engine: "python"
12
- code: |
13
- import os
14
-
15
- # Assume these build functions are available in the execution environment
16
- # from a larger project context, e.g., from npcpy.build_funcs
17
- try:
18
- from npcpy.build_funcs import (
19
- build_flask_server,
20
- build_docker_compose,
21
- build_cli_executable,
22
- build_static_site,
23
- )
24
- except ImportError:
25
- # Provide mock functions for demonstration or error handling
26
- def build_flask_server(config, **kwargs): return {"output": f"Mock build flask: {config}", "messages": []}
27
- def build_docker_compose(config, **kwargs): return {"output": f"Mock build docker: {config}", "messages": []}
28
- def build_cli_executable(config, **kwargs): return {"output": f"Mock build cli: {config}", "messages": []}
29
- def build_static_site(config, **kwargs): return {"output": f"Mock build static: {config}", "messages": []}
30
-
31
- target = context.get('target') or 'flask'
32
- output_dir = context.get('outdir') or './build'
33
- team_path = context.get('team') or './npc_team'
34
- port = context.get('port') or 5337
35
- cors_origins_str = context.get('cors') or ''
36
-
37
- cors_origins = [origin.strip() for origin in cors_origins_str.split(',') if origin.strip()] or None
38
-
39
- build_config = {
40
- 'team_path': os.path.abspath(os.path.expanduser(team_path)),
41
- 'output_dir': os.path.abspath(os.path.expanduser(output_dir)),
42
- 'target': target,
43
- 'port': port,
44
- 'cors_origins': cors_origins,
45
- }
46
-
47
- builders = {
48
- 'flask': build_flask_server,
49
- 'docker': build_docker_compose,
50
- 'cli': build_cli_executable,
51
- 'static': build_static_site,
52
- }
53
-
54
- output_messages = context.get('messages', [])
55
- output_result = ""
56
-
57
- if target not in builders:
58
- output_result = f"Unknown target: {target}. Available: {list(builders.keys())}"
59
- else:
60
- result = builders[target](build_config, messages=output_messages)
61
- output_result = result.get('output', 'Build command executed.')
62
- output_messages = result.get('messages', output_messages) # Update messages from builder call
63
-
64
- context['output'] = output_result
65
- context['messages'] = output_messages
@@ -1,23 +0,0 @@
1
- name: plonkjr
2
- ascii_art: |
3
- 🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲
4
- ██████ ██ ██████ ███ ██ ██ ██ 🪵 ██ ██████
5
- ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██
6
- ██████ ██ ██ ██ ██ ██ ██ █████ 🌲 ██ ██████
7
- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
8
- ██ ███████ ██████ ██ ████ ██ ██ █████ ██ ██
9
- 🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲
10
- colors:
11
- top: "34,139,34"
12
- bottom: "139,69,19"
13
- primary_directive: |
14
- You are plonkjr, the junior automation assistant. You handle basic computer use tasks:
15
- taking screenshots, clicking, typing, and pressing keys. You're simpler than plonk -
16
- you focus on executing the core actions without complex vision-based planning.
17
- Just do what's asked: screenshot, click, type, key press.
18
- jinxs:
19
- - lib/computer_use/screenshot
20
- - lib/computer_use/click
21
- - lib/computer_use/type_text
22
- - lib/computer_use/key_press
23
- - lib/core/sh