npcsh 1.1.16__py3-none-any.whl → 1.1.17__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 (176) hide show
  1. npcsh/_state.py +24 -9
  2. npcsh/benchmark/__init__.py +22 -0
  3. npcsh/benchmark/npcsh_agent.py +262 -0
  4. npcsh/benchmark/runner.py +569 -0
  5. npcsh/npc_team/jinxs/bin/benchmark.jinx +146 -0
  6. npcsh/npc_team/jinxs/bin/nql.jinx +7 -7
  7. npcsh/npc_team/jinxs/bin/roll.jinx +20 -23
  8. npcsh/npc_team/jinxs/bin/sample.jinx +6 -7
  9. npcsh/npc_team/jinxs/bin/spool.jinx +4 -4
  10. npcsh/npc_team/jinxs/bin/sync.jinx +6 -6
  11. npcsh/npc_team/jinxs/bin/vixynt.jinx +8 -8
  12. npcsh/npc_team/jinxs/bin/wander.jinx +109 -19
  13. npcsh/npc_team/jinxs/bin/yap.jinx +5 -5
  14. npcsh/npc_team/jinxs/incognide/add_tab.jinx +11 -0
  15. npcsh/npc_team/jinxs/incognide/close_pane.jinx +9 -0
  16. npcsh/npc_team/jinxs/incognide/close_tab.jinx +10 -0
  17. npcsh/npc_team/jinxs/incognide/confirm.jinx +10 -0
  18. npcsh/npc_team/jinxs/incognide/focus_pane.jinx +9 -0
  19. npcsh/npc_team/jinxs/{npc_studio/npc-studio.jinx → incognide/incognide.jinx} +2 -2
  20. npcsh/npc_team/jinxs/incognide/list_panes.jinx +8 -0
  21. npcsh/npc_team/jinxs/incognide/navigate.jinx +10 -0
  22. npcsh/npc_team/jinxs/incognide/notify.jinx +10 -0
  23. npcsh/npc_team/jinxs/incognide/open_pane.jinx +13 -0
  24. npcsh/npc_team/jinxs/incognide/read_pane.jinx +9 -0
  25. npcsh/npc_team/jinxs/incognide/run_terminal.jinx +10 -0
  26. npcsh/npc_team/jinxs/incognide/send_message.jinx +10 -0
  27. npcsh/npc_team/jinxs/incognide/split_pane.jinx +12 -0
  28. npcsh/npc_team/jinxs/incognide/switch_npc.jinx +10 -0
  29. npcsh/npc_team/jinxs/incognide/switch_tab.jinx +10 -0
  30. npcsh/npc_team/jinxs/incognide/write_file.jinx +11 -0
  31. npcsh/npc_team/jinxs/incognide/zen_mode.jinx +9 -0
  32. npcsh/npc_team/jinxs/lib/browser/browser_action.jinx +4 -4
  33. npcsh/npc_team/jinxs/lib/browser/browser_screenshot.jinx +1 -1
  34. npcsh/npc_team/jinxs/lib/browser/open_browser.jinx +2 -2
  35. npcsh/npc_team/jinxs/lib/computer_use/click.jinx +2 -2
  36. npcsh/npc_team/jinxs/lib/computer_use/key_press.jinx +1 -1
  37. npcsh/npc_team/jinxs/lib/computer_use/launch_app.jinx +1 -1
  38. npcsh/npc_team/jinxs/lib/computer_use/screenshot.jinx +1 -1
  39. npcsh/npc_team/jinxs/lib/computer_use/trigger.jinx +2 -2
  40. npcsh/npc_team/jinxs/lib/computer_use/type_text.jinx +1 -1
  41. npcsh/npc_team/jinxs/lib/computer_use/wait.jinx +1 -1
  42. npcsh/npc_team/jinxs/lib/core/chat.jinx +4 -4
  43. npcsh/npc_team/jinxs/lib/core/cmd.jinx +4 -4
  44. npcsh/npc_team/jinxs/lib/core/compress.jinx +8 -8
  45. npcsh/npc_team/jinxs/lib/core/edit_file.jinx +3 -0
  46. npcsh/npc_team/jinxs/lib/core/ots.jinx +7 -7
  47. npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +44 -0
  48. npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +94 -0
  49. npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +96 -0
  50. npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +80 -0
  51. npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +51 -0
  52. npcsh/npc_team/jinxs/lib/core/search.jinx +52 -129
  53. npcsh/npc_team/jinxs/lib/core/sh.jinx +1 -1
  54. npcsh/npc_team/jinxs/lib/core/sleep.jinx +7 -7
  55. npcsh/npc_team/jinxs/lib/core/sql.jinx +7 -7
  56. npcsh/npc_team/jinxs/lib/orchestration/convene.jinx +7 -7
  57. npcsh/npc_team/jinxs/lib/orchestration/delegate.jinx +8 -9
  58. npcsh/npc_team/jinxs/lib/research/arxiv.jinx +2 -2
  59. npcsh/npc_team/jinxs/lib/research/paper_search.jinx +3 -3
  60. npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +2 -2
  61. npcsh/npc_team/jinxs/lib/utils/build.jinx +5 -5
  62. npcsh/npc_team/jinxs/lib/utils/compile.jinx +2 -2
  63. npcsh/npc_team/jinxs/lib/utils/help.jinx +1 -1
  64. npcsh/npc_team/jinxs/lib/utils/init.jinx +5 -5
  65. npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +1 -1
  66. npcsh/npc_team/jinxs/lib/utils/serve.jinx +2 -2
  67. npcsh/npc_team/jinxs/lib/utils/set.jinx +2 -2
  68. npcsh/npc_team/jinxs/lib/utils/switch.jinx +3 -3
  69. npcsh/npc_team/jinxs/lib/utils/switches.jinx +1 -1
  70. npcsh/npc_team/jinxs/lib/utils/teamviz.jinx +2 -2
  71. npcsh/npc_team/sibiji.npc +1 -1
  72. npcsh/npcsh.py +81 -43
  73. npcsh-1.1.17.data/data/npcsh/npc_team/add_tab.jinx +11 -0
  74. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/arxiv.jinx +2 -2
  75. npcsh-1.1.17.data/data/npcsh/npc_team/benchmark.jinx +146 -0
  76. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/browser_action.jinx +4 -4
  77. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/browser_screenshot.jinx +1 -1
  78. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/build.jinx +5 -5
  79. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/chat.jinx +4 -4
  80. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/click.jinx +2 -2
  81. npcsh-1.1.17.data/data/npcsh/npc_team/close_pane.jinx +9 -0
  82. npcsh-1.1.17.data/data/npcsh/npc_team/close_tab.jinx +10 -0
  83. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/cmd.jinx +4 -4
  84. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/compile.jinx +2 -2
  85. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/compress.jinx +8 -8
  86. npcsh-1.1.17.data/data/npcsh/npc_team/confirm.jinx +10 -0
  87. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/convene.jinx +7 -7
  88. npcsh-1.1.17.data/data/npcsh/npc_team/db_search.jinx +44 -0
  89. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/delegate.jinx +8 -9
  90. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/edit_file.jinx +3 -0
  91. npcsh-1.1.17.data/data/npcsh/npc_team/file_search.jinx +94 -0
  92. npcsh-1.1.17.data/data/npcsh/npc_team/focus_pane.jinx +9 -0
  93. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/help.jinx +1 -1
  94. npcsh-1.1.16.data/data/npcsh/npc_team/npc-studio.jinx → npcsh-1.1.17.data/data/npcsh/npc_team/incognide.jinx +2 -2
  95. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/init.jinx +5 -5
  96. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/jinxs.jinx +1 -1
  97. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/key_press.jinx +1 -1
  98. npcsh-1.1.17.data/data/npcsh/npc_team/kg_search.jinx +96 -0
  99. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/launch_app.jinx +1 -1
  100. npcsh-1.1.17.data/data/npcsh/npc_team/list_panes.jinx +8 -0
  101. npcsh-1.1.17.data/data/npcsh/npc_team/mem_search.jinx +80 -0
  102. npcsh-1.1.17.data/data/npcsh/npc_team/navigate.jinx +10 -0
  103. npcsh-1.1.17.data/data/npcsh/npc_team/notify.jinx +10 -0
  104. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/nql.jinx +7 -7
  105. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/open_browser.jinx +2 -2
  106. npcsh-1.1.17.data/data/npcsh/npc_team/open_pane.jinx +13 -0
  107. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/ots.jinx +7 -7
  108. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/paper_search.jinx +3 -3
  109. npcsh-1.1.17.data/data/npcsh/npc_team/read_pane.jinx +9 -0
  110. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/roll.jinx +20 -23
  111. npcsh-1.1.17.data/data/npcsh/npc_team/run_terminal.jinx +10 -0
  112. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/sample.jinx +6 -7
  113. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/screenshot.jinx +1 -1
  114. npcsh-1.1.17.data/data/npcsh/npc_team/search.jinx +54 -0
  115. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/semantic_scholar.jinx +2 -2
  116. npcsh-1.1.17.data/data/npcsh/npc_team/send_message.jinx +10 -0
  117. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/serve.jinx +2 -2
  118. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/set.jinx +2 -2
  119. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/sh.jinx +1 -1
  120. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/sibiji.npc +1 -1
  121. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/sleep.jinx +7 -7
  122. npcsh-1.1.17.data/data/npcsh/npc_team/split_pane.jinx +12 -0
  123. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/spool.jinx +4 -4
  124. npcsh-1.1.17.data/data/npcsh/npc_team/sql.jinx +16 -0
  125. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/switch.jinx +3 -3
  126. npcsh-1.1.17.data/data/npcsh/npc_team/switch_npc.jinx +10 -0
  127. npcsh-1.1.17.data/data/npcsh/npc_team/switch_tab.jinx +10 -0
  128. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/switches.jinx +1 -1
  129. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/sync.jinx +6 -6
  130. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/teamviz.jinx +2 -2
  131. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/trigger.jinx +2 -2
  132. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/type_text.jinx +1 -1
  133. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/vixynt.jinx +8 -8
  134. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/wait.jinx +1 -1
  135. npcsh-1.1.17.data/data/npcsh/npc_team/wander.jinx +242 -0
  136. npcsh-1.1.17.data/data/npcsh/npc_team/web_search.jinx +51 -0
  137. npcsh-1.1.17.data/data/npcsh/npc_team/write_file.jinx +11 -0
  138. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/yap.jinx +5 -5
  139. npcsh-1.1.17.data/data/npcsh/npc_team/zen_mode.jinx +9 -0
  140. {npcsh-1.1.16.dist-info → npcsh-1.1.17.dist-info}/METADATA +10 -7
  141. npcsh-1.1.17.dist-info/RECORD +219 -0
  142. {npcsh-1.1.16.dist-info → npcsh-1.1.17.dist-info}/entry_points.txt +2 -0
  143. npcsh-1.1.16.data/data/npcsh/npc_team/search.jinx +0 -131
  144. npcsh-1.1.16.data/data/npcsh/npc_team/sql.jinx +0 -16
  145. npcsh-1.1.16.data/data/npcsh/npc_team/wander.jinx +0 -152
  146. npcsh-1.1.16.dist-info/RECORD +0 -170
  147. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/alicanto.npc +0 -0
  148. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/alicanto.png +0 -0
  149. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  150. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/corca.npc +0 -0
  151. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/corca.png +0 -0
  152. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/corca_example.png +0 -0
  153. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/frederic.npc +0 -0
  154. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/frederic4.png +0 -0
  155. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/guac.npc +0 -0
  156. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/guac.png +0 -0
  157. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
  158. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  159. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  160. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  161. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  162. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/paste.jinx +0 -0
  163. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/plonk.npc +0 -0
  164. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/plonk.png +0 -0
  165. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/plonkjr.npc +0 -0
  166. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  167. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/python.jinx +0 -0
  168. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/shh.jinx +0 -0
  169. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/sibiji.png +0 -0
  170. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/spool.png +0 -0
  171. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/usage.jinx +0 -0
  172. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  173. {npcsh-1.1.16.data → npcsh-1.1.17.data}/data/npcsh/npc_team/yap.png +0 -0
  174. {npcsh-1.1.16.dist-info → npcsh-1.1.17.dist-info}/WHEEL +0 -0
  175. {npcsh-1.1.16.dist-info → npcsh-1.1.17.dist-info}/licenses/LICENSE +0 -0
  176. {npcsh-1.1.16.dist-info → npcsh-1.1.17.dist-info}/top_level.txt +0 -0
npcsh/npcsh.py CHANGED
@@ -180,51 +180,54 @@ def run_repl(command_history: CommandHistory, initial_state: ShellState, router,
180
180
  def exit_shell(current_state: ShellState):
181
181
  print("\nGoodbye!")
182
182
  print(colored("Processing and archiving all session knowledge...", "cyan"))
183
-
184
- engine = command_history.engine
185
183
 
186
- for team_name, npc_name, path in session_scopes:
187
- try:
188
- print(f" -> Archiving knowledge for: T='{team_name}', N='{npc_name}', P='{path}'")
189
-
190
- convo_id = current_state.conversation_id
191
- all_messages = command_history.get_conversations_by_id(convo_id)
192
-
193
- scope_messages = [
194
- m for m in all_messages
195
- if m.get('directory_path') == path and m.get('team') == team_name and m.get('npc') == npc_name
196
- ]
197
-
198
- full_text = "\n".join([f"{m['role']}: {m['content']}" for m in scope_messages if m.get('content')])
199
-
200
- if not full_text.strip():
201
- print(" ...No content for this scope, skipping.")
202
- continue
184
+ engine = command_history.engine
203
185
 
204
- current_kg = load_kg_from_db(engine, team_name, npc_name, path)
205
-
206
- evolved_kg, _ = kg_evolve_incremental(
207
- existing_kg=current_kg,
208
- new_content_text=full_text,
209
- model=current_state.npc.model,
210
- provider=current_state.npc.provider,
211
- npc= current_state.npc,
212
- get_concepts=True,
213
- link_concepts_facts = True,
214
- link_concepts_concepts = True,
215
- link_facts_facts = True,
216
- )
217
-
218
- save_kg_to_db(engine,
219
- evolved_kg,
220
- team_name,
221
- npc_name,
222
- path)
223
-
224
- except Exception as e:
225
- import traceback
226
- print(colored(f"Failed to process KG for scope ({team_name}, {npc_name}, {path}): {e}", "red"))
227
- traceback.print_exc()
186
+ try:
187
+ for team_name, npc_name, path in session_scopes:
188
+ try:
189
+ print(f" -> Archiving knowledge for: T='{team_name}', N='{npc_name}', P='{path}'")
190
+
191
+ convo_id = current_state.conversation_id
192
+ all_messages = command_history.get_conversations_by_id(convo_id)
193
+
194
+ scope_messages = [
195
+ m for m in all_messages
196
+ if m.get('directory_path') == path and m.get('team') == team_name and m.get('npc') == npc_name
197
+ ]
198
+
199
+ full_text = "\n".join([f"{m['role']}: {m['content']}" for m in scope_messages if m.get('content')])
200
+
201
+ if not full_text.strip():
202
+ print(" ...No content for this scope, skipping.")
203
+ continue
204
+
205
+ current_kg = load_kg_from_db(engine, team_name, npc_name, path)
206
+
207
+ evolved_kg, _ = kg_evolve_incremental(
208
+ existing_kg=current_kg,
209
+ new_content_text=full_text,
210
+ model=current_state.npc.model,
211
+ provider=current_state.npc.provider,
212
+ npc= current_state.npc,
213
+ get_concepts=True,
214
+ link_concepts_facts = True,
215
+ link_concepts_concepts = True,
216
+ link_facts_facts = True,
217
+ )
218
+
219
+ save_kg_to_db(engine,
220
+ evolved_kg,
221
+ team_name,
222
+ npc_name,
223
+ path)
224
+
225
+ except Exception as e:
226
+ import traceback
227
+ print(colored(f"Failed to process KG for scope ({team_name}, {npc_name}, {path}): {e}", "red"))
228
+ traceback.print_exc()
229
+ except KeyboardInterrupt:
230
+ print(colored("\nSkipping knowledge archival.", "yellow"))
228
231
 
229
232
  sys.exit(0)
230
233
 
@@ -381,8 +384,43 @@ def main(npc_name: str = None) -> None:
381
384
  parser.add_argument(
382
385
  "-n", "--npc", type=str, help="Start with a specific NPC active."
383
386
  )
387
+ parser.add_argument(
388
+ "--refresh", action="store_true", help="Force refresh of NPCs and jinxs from package."
389
+ )
384
390
  args = parser.parse_args()
385
391
 
392
+ # Handle refresh flag - reset initialization and re-copy files
393
+ if args.refresh:
394
+ from npcsh._state import initialize_base_npcs_if_needed
395
+ import shutil
396
+
397
+ npcshrc_path = os.path.expanduser("~/.npcshrc")
398
+ if os.path.exists(npcshrc_path):
399
+ with open(npcshrc_path, "r") as f:
400
+ content = f.read()
401
+ content = content.replace("export NPCSH_INITIALIZED=1", "export NPCSH_INITIALIZED=0")
402
+ with open(npcshrc_path, "w") as f:
403
+ f.write(content)
404
+
405
+ os.environ["NPCSH_INITIALIZED"] = "0"
406
+
407
+ # Remove existing jinxs and NPCs to force fresh copy
408
+ user_npc_team = os.path.expanduser("~/.npcsh/npc_team")
409
+ jinxs_dir = os.path.join(user_npc_team, "jinxs")
410
+ if os.path.exists(jinxs_dir):
411
+ shutil.rmtree(jinxs_dir)
412
+ print("Cleared existing jinxs directory")
413
+
414
+ for f in os.listdir(user_npc_team) if os.path.exists(user_npc_team) else []:
415
+ if f.endswith(".npc"):
416
+ os.remove(os.path.join(user_npc_team, f))
417
+ print(f"Removed {f}")
418
+
419
+ db_path = os.path.expanduser("~/.npcsh/npcsh_history.db")
420
+ print("Reinitializing NPCs and jinxs...")
421
+ initialize_base_npcs_if_needed(db_path)
422
+ print("Refresh complete!")
423
+
386
424
  command_history, team, default_npc = setup_shell()
387
425
 
388
426
  if team and hasattr(team, 'jinxs_dict'):
@@ -0,0 +1,11 @@
1
+ jinx_name: studio.add_tab
2
+ description: Add a new tab to a pane.
3
+ inputs:
4
+ - paneId: "active"
5
+ - type: ""
6
+ - path: ""
7
+ steps:
8
+ - name: frontend_action
9
+ engine: python
10
+ code: |
11
+ context['output'] = "Action executed by frontend"
@@ -1,8 +1,8 @@
1
1
  jinx_name: arxiv
2
2
  description: Search arXiv for preprints and papers
3
3
  inputs:
4
- - query: ""
5
- - limit: 10
4
+ - query: ""
5
+ - limit: 10
6
6
  steps:
7
7
  - name: search_arxiv
8
8
  engine: python
@@ -0,0 +1,146 @@
1
+ jinx_name: benchmark
2
+ description: Run Terminal-Bench evaluation to benchmark npcsh performance with different models
3
+ inputs:
4
+ - model: ""
5
+ - provider: ""
6
+ - action: "check"
7
+ - concurrent: "4"
8
+ - npc_name: ""
9
+
10
+ steps:
11
+ - name: run_benchmark
12
+ engine: python
13
+ code: |
14
+ import os
15
+ import sys
16
+
17
+ action = {{ action | default("check") | tojson }}.strip().lower()
18
+ model = {{ model | default("") | tojson }}.strip()
19
+ provider = {{ provider | default("") | tojson }}.strip()
20
+ concurrent = int({{ concurrent | default("4") | tojson }} or "4")
21
+ npc_name_input = {{ npc_name | default("") | tojson }}.strip() or None
22
+
23
+ if not model:
24
+ model = npc.model if npc and npc.model
25
+ if not provider:
26
+ provider = npc.provider if npc and npc.provider else "anthropic"
27
+
28
+ try:
29
+ from npcsh.benchmark import BenchmarkRunner, run_benchmark
30
+ BENCHMARK_AVAILABLE = True
31
+ except ImportError:
32
+ BENCHMARK_AVAILABLE = False
33
+
34
+ if action == "check":
35
+ output = "## Terminal-Bench Integration Status\n\n"
36
+
37
+ if not BENCHMARK_AVAILABLE:
38
+ output += "**Status:** Benchmark module not fully loaded (harbor not installed)\n\n"
39
+ else:
40
+ output += "**Status:** Ready\n\n"
41
+
42
+ if BENCHMARK_AVAILABLE:
43
+ runner = BenchmarkRunner()
44
+ deps = runner.check_dependencies()
45
+ output += "### Dependencies:\n"
46
+ for dep, installed in deps.items():
47
+ status = "Installed" if installed else "Not installed"
48
+ output += "- **{}**: {}\n".format(dep, status)
49
+
50
+ if not all(deps.values()):
51
+ output += "\n### Installation:\n"
52
+ output += "```bash\n"
53
+ output += "pip install harbor terminal-bench\n"
54
+ output += "```\n"
55
+
56
+ output += "\n### Usage:\n"
57
+ output += "```\n"
58
+ output += "/benchmark action=quick\n"
59
+ output += "/benchmark action=run model=gpt-4o provider=openai\n"
60
+ output += "/benchmark action=list\n"
61
+ output += "```\n"
62
+
63
+ elif action == "list":
64
+ if not BENCHMARK_AVAILABLE:
65
+ output = "Error: Benchmark module not available. Run `/benchmark` first."
66
+ else:
67
+ runner = BenchmarkRunner()
68
+ runs = runner.list_past_runs()
69
+
70
+ if not runs:
71
+ output = "No past benchmark runs found."
72
+ else:
73
+ output = "## Past Benchmark Runs ({} total)\n\n".format(len(runs))
74
+ for run in runs[:10]:
75
+ timestamp = run.get('timestamp', 'unknown')[:19]
76
+ model_name = run.get('model', 'unknown')
77
+ result = run.get('result', {})
78
+ accuracy = result.get('accuracy', 0)
79
+ passed = result.get('passed_tasks', 0)
80
+ total = result.get('total_tasks', 0)
81
+
82
+ output += "### {}\n".format(timestamp)
83
+ output += "- **Model:** {}\n".format(model_name)
84
+ output += "- **Accuracy:** {:.1%}\n".format(accuracy)
85
+ output += "- **Tasks:** {}/{}\n\n".format(passed, total)
86
+
87
+ elif action == "quick":
88
+ if not BENCHMARK_AVAILABLE:
89
+ output = "Error: Install with: pip install harbor terminal-bench"
90
+ else:
91
+ output = "## Quick Test: {}/{}\n\n".format(provider, model)
92
+ output += "Running quick test with 3 tasks...\n\n"
93
+
94
+ try:
95
+ from npcsh.benchmark import quick_test
96
+ result = quick_test(model=model, provider=provider)
97
+
98
+ status = "PASS" if result.success else "FAIL"
99
+ output += "**Status:** {}\n".format(status)
100
+ output += "**Accuracy:** {:.1%}\n".format(result.accuracy)
101
+ output += "**Tasks:** {}/{}\n".format(result.passed_tasks, result.total_tasks)
102
+ output += "**Duration:** {:.1f}s\n".format(result.duration_seconds)
103
+
104
+ if result.error:
105
+ output += "\n**Error:** {}\n".format(result.error)
106
+
107
+ output += "\n**Output:** {}\n".format(result.output_dir)
108
+
109
+ except Exception as e:
110
+ output = "Error running quick test: {}".format(e)
111
+
112
+ elif action == "run":
113
+ if not BENCHMARK_AVAILABLE:
114
+ output = "Error: Install with: pip install harbor terminal-bench"
115
+ else:
116
+ output = "## Benchmark Run: {}/{}\n\n".format(provider, model)
117
+ output += "Running Terminal-Bench 2.0 with {} concurrent tasks...\n\n".format(concurrent)
118
+
119
+ try:
120
+ runner = BenchmarkRunner()
121
+ result = runner.run(
122
+ model=model,
123
+ provider=provider,
124
+ n_concurrent=concurrent,
125
+ npc_name=npc_name_input,
126
+ )
127
+
128
+ status = "SUCCESS" if result.success else "FAILED"
129
+ output += "**Status:** {}\n".format(status)
130
+ output += "**Accuracy:** {:.1%}\n".format(result.accuracy)
131
+ output += "**Tasks Passed:** {}/{}\n".format(result.passed_tasks, result.total_tasks)
132
+ output += "**Duration:** {:.1f}s\n".format(result.duration_seconds)
133
+ output += "**Total Tokens:** {:,}\n".format(result.total_tokens)
134
+ output += "**Total Cost:** ${:.4f}\n".format(result.total_cost_usd)
135
+
136
+ if result.error:
137
+ output += "\n**Error:** {}\n".format(result.error)
138
+
139
+ output += "\n**Results saved to:** {}\n".format(result.output_dir)
140
+
141
+ except Exception as e:
142
+ import traceback
143
+ output = "Error running benchmark: {}\n\n{}".format(e, traceback.format_exc())
144
+
145
+ else:
146
+ output = "Unknown action: {}\n\nAvailable: check, run, quick, list".format(action)
@@ -12,14 +12,14 @@ description: |
12
12
  - get_page: Get page title, URL, and visible text
13
13
  - get_elements: Get interactive elements with their selectors
14
14
  - press_key: Press a key (enter, tab, escape, etc)
15
- Selectors: CSS (#id, .class, input[name="x"]) or xpath://... for XPath
15
+ Selectors: CSS (
16
16
  inputs:
17
- - action:
17
+ - action:
18
18
  description: "Action: click, type, type_and_enter, set_value, select, wait, scroll, get_text, get_page, get_elements, press_key"
19
- - selector:
19
+ - selector:
20
20
  description: "CSS selector or XPath (prefix xpath: for XPath)"
21
21
  default: ""
22
- - value:
22
+ - value:
23
23
  description: "Value for type/select, or scroll direction, or key name"
24
24
  default: ""
25
25
 
@@ -1,7 +1,7 @@
1
1
  jinx_name: browser_screenshot
2
2
  description: Take a screenshot of the current browser page.
3
3
  inputs:
4
- - filename:
4
+ - filename:
5
5
  description: "Optional filename for screenshot"
6
6
  default: ""
7
7
 
@@ -1,11 +1,11 @@
1
1
  jinx_name: "build"
2
2
  description: "Build deployment artifacts for NPC team"
3
3
  inputs:
4
- - target: "flask" # The type of deployment target (e.g., flask, docker, cli, static).
5
- - outdir: "./build" # The output directory for built artifacts.
6
- - team: "./npc_team" # The path to the NPC team directory.
7
- - port: 5337 # The port for flask server builds.
8
- - cors: "" # Comma-separated CORS origins for flask server builds.
4
+ - target: "flask"
5
+ - outdir: "./build"
6
+ - team: "./npc_team"
7
+ - port: 5337
8
+ - cors: ""
9
9
  steps:
10
10
  - name: "execute_build"
11
11
  engine: "python"
@@ -1,10 +1,10 @@
1
1
  jinx_name: chat
2
2
  description: Simple chat mode - LLM conversation without tool execution
3
3
  inputs:
4
- - query: null
5
- - model: null
6
- - provider: null
7
- - stream: true
4
+ - query: null
5
+ - model: null
6
+ - provider: null
7
+ - stream: true
8
8
 
9
9
  steps:
10
10
  - name: chat_response
@@ -1,8 +1,8 @@
1
1
  jinx_name: click
2
2
  description: Click at screen coordinates (0-100 percentage)
3
3
  inputs:
4
- - x: 50 # X coordinate as percentage (0-100)
5
- - y: 50 # Y coordinate as percentage (0-100)
4
+ - x: 50
5
+ - y: 50
6
6
 
7
7
  steps:
8
8
  - name: perform_click
@@ -0,0 +1,9 @@
1
+ jinx_name: studio.close_pane
2
+ description: Close a pane in NPC Studio. Use paneId="active" or omit to close the active pane.
3
+ inputs:
4
+ - paneId: "active"
5
+ steps:
6
+ - name: frontend_action
7
+ engine: python
8
+ code: |
9
+ context['output'] = "Action executed by frontend"
@@ -0,0 +1,10 @@
1
+ jinx_name: studio.close_tab
2
+ description: Close a specific tab in a pane.
3
+ inputs:
4
+ - paneId: "active"
5
+ - tabIndex: 0
6
+ steps:
7
+ - name: frontend_action
8
+ engine: python
9
+ code: |
10
+ context['output'] = "Action executed by frontend"
@@ -1,10 +1,10 @@
1
1
  jinx_name: cmd
2
2
  description: Command mode - LLM generates and executes shell commands
3
3
  inputs:
4
- - query: null
5
- - model: null
6
- - provider: null
7
- - stream: true
4
+ - query: null
5
+ - model: null
6
+ - provider: null
7
+ - stream: true
8
8
 
9
9
  steps:
10
10
  - name: cmd_execute
@@ -1,8 +1,8 @@
1
1
  jinx_name: "compile"
2
2
  description: "Compile NPC profiles"
3
3
  inputs:
4
- - npc_file_path: "" # Optional path to a specific NPC file to compile.
5
- - npc_team_dir: "./npc_team" # Directory containing NPC profiles to compile, if no specific file is given.
4
+ - npc_file_path: ""
5
+ - npc_team_dir: "./npc_team"
6
6
  steps:
7
7
  - name: "compile_npcs"
8
8
  engine: "python"
@@ -1,12 +1,12 @@
1
1
  jinx_name: "compress"
2
2
  description: "Manages conversation and knowledge context. Defaults to compacting context. Use flags for other operations."
3
3
  inputs:
4
- - flush: "" # The number of recent messages to flush.
5
- - sleep: False # If true, evolves the knowledge graph.
6
- - dream: False # Used with --sleep. Runs creative synthesis.
7
- - ops: "" # Used with --sleep. Comma-separated list of KG operations.
8
- - model: "" # Used with --sleep. LLM model for KG evolution.
9
- - provider: "" # Used with --sleep. LLM provider for KG evolution.
4
+ - flush: ""
5
+ - sleep: False
6
+ - dream: False
7
+ - ops: ""
8
+ - model: ""
9
+ - provider: ""
10
10
  steps:
11
11
  - name: "manage_context_and_memory"
12
12
  engine: "python"
@@ -53,8 +53,8 @@ steps:
53
53
  operations_config = [op.strip() for op in operations_str.split(',')] if operations_str else None
54
54
  if not llm_model and current_npc: llm_model = current_npc.model
55
55
  if not llm_provider and current_npc: llm_provider = current_npc.provider
56
- if not llm_model: llm_model = "gemini-1.5-pro"
57
- if not llm_provider: llm_provider = "gemini"
56
+ if not llm_model: llm_model = state.chat_model if state else "llama3.2"
57
+ if not llm_provider: llm_provider = state.chat_provider if state else "ollama"
58
58
 
59
59
  team_name = current_team.name if current_team else "__none__"
60
60
  npc_name = current_npc.name if current_npc else "__none__"
@@ -0,0 +1,10 @@
1
+ jinx_name: studio.confirm
2
+ description: Show a confirmation dialog and return the user's choice.
3
+ inputs:
4
+ - message: ""
5
+ - title: "Confirm"
6
+ steps:
7
+ - name: frontend_action
8
+ engine: python
9
+ code: |
10
+ context['output'] = "Action executed by frontend"
@@ -1,11 +1,11 @@
1
1
  jinx_name: convene
2
2
  description: Run a cycle of discussions between NPCs on a topic. The orchestrator convenes agents to discuss and synthesize.
3
3
  inputs:
4
- - topic: ""
5
- - npcs: "alicanto,corca,guac"
6
- - rounds: 3
7
- - model: null
8
- - provider: null
4
+ - topic: ""
5
+ - npcs: "alicanto,corca,guac"
6
+ - rounds: 3
7
+ - model: null
8
+ - provider: null
9
9
  steps:
10
10
  - name: convene_discussion
11
11
  engine: python
@@ -21,8 +21,8 @@ steps:
21
21
  team = context.get('team')
22
22
  messages = context.get('messages', [])
23
23
 
24
- model = context.get('model') or (npc.model if npc else 'gemini-1.5-flash')
25
- provider = context.get('provider') or (npc.provider if npc else 'gemini')
24
+ model = context.get('model') or (npc.model if npc else (state.chat_model if state else 'llama3.2'))
25
+ provider = context.get('provider') or (npc.provider if npc else (state.chat_provider if state else 'ollama'))
26
26
 
27
27
  if not topic:
28
28
  context['output'] = """Usage: /convene <topic>
@@ -0,0 +1,44 @@
1
+ jinx_name: db_search
2
+ description: Search conversation history database using brainblast
3
+ inputs:
4
+ - query: ""
5
+ - db_path: ""
6
+ - limit: "20"
7
+
8
+ steps:
9
+ - name: search_db
10
+ engine: python
11
+ code: |
12
+ import os
13
+
14
+ query = context.get('query', '').strip()
15
+ if not query:
16
+ lines = [
17
+ "Usage: /db_search <query>",
18
+ "",
19
+ "Searches conversation history using brainblast for semantic matching.",
20
+ "",
21
+ "Options:",
22
+ " db_path - Path to history database",
23
+ " limit - Max results to return (default 20)",
24
+ "",
25
+ "Examples:",
26
+ " /db_search python debugging",
27
+ " /db_search api errors limit=50",
28
+ ]
29
+ context['output'] = "\n".join(lines)
30
+ else:
31
+ db_path = context.get('db_path') or os.path.expanduser("~/.npcsh/npcsh_history.db")
32
+ limit = int(context.get('limit') or 20)
33
+
34
+ try:
35
+ cmd_history = CommandHistory(db_path)
36
+ result = execute_brainblast_command(
37
+ command=query,
38
+ command_history=cmd_history,
39
+ limit=limit
40
+ )
41
+ context['output'] = result.get('output', 'Brainblast search completed.')
42
+ except Exception as e:
43
+ import traceback
44
+ context['output'] = "DB search error: " + str(e) + "\n" + traceback.format_exc()
@@ -1,11 +1,11 @@
1
1
  jinx_name: delegate
2
2
  description: Delegate a task to another NPC with review and feedback loop until completion. Choose the NPC whose directive best matches the task.
3
3
  inputs:
4
- - npc_name:
4
+ - npc_name:
5
5
  description: "Name of the NPC to delegate to"
6
- - task:
6
+ - task:
7
7
  description: "The task or request to delegate to the NPC"
8
- - max_iterations: "10"
8
+ - max_iterations: "10"
9
9
  steps:
10
10
  - name: delegate_with_review
11
11
  engine: python
@@ -43,8 +43,7 @@ steps:
43
43
  sep = '-' * 60
44
44
  print(colored("\n" + sep, "cyan"))
45
45
  print(colored(" Delegating to @" + target_name, "yellow", attrs=["bold"]))
46
- task_preview = task_request[:100] + ('...' if len(task_request) > 100 else '')
47
- print(colored(" Task: " + task_preview, "white", attrs=["dark"]))
46
+ print(colored(" Task: " + task_request, "white", attrs=["dark"]))
48
47
  print(colored(sep + "\n", "cyan"))
49
48
  print(colored(" [{}] Model: {}".format(target_name, target_npc.model), "white", attrs=["dark"]))
50
49
  jinx_list = ', '.join(list(target_jinxs.keys())[:8])
@@ -127,8 +126,8 @@ steps:
127
126
 
128
127
  review_result = get_llm_response(
129
128
  review_prompt,
130
- model=getattr(orchestrator, 'model', 'gemini-2.5-flash'),
131
- provider=getattr(orchestrator, 'provider', 'gemini'),
129
+ model=getattr(orchestrator, 'model', None) or (state.chat_model if state else 'llama3.2'),
130
+ provider=getattr(orchestrator, 'provider', None) or (state.chat_provider if state else 'ollama'),
132
131
  npc=orchestrator,
133
132
  temperature=0.3
134
133
  )
@@ -150,12 +149,12 @@ steps:
150
149
  task_complete = True
151
150
  print(colored("\n Task completed successfully", "green", attrs=["bold"]))
152
151
  if summary:
153
- print(colored(" Summary: " + summary[:200], "white", attrs=["dark"]))
152
+ print(colored(" Summary: " + summary, "white", attrs=["dark"]))
154
153
  final_output = "[{}] Task completed.\n{}".format(target_name, summary)
155
154
  else:
156
155
  print(colored("\n Task incomplete - providing feedback", "yellow"))
157
156
  if feedback:
158
- print(colored(" Feedback: " + feedback[:200] + "...", "white", attrs=["dark"]))
157
+ print(colored(" Feedback: " + feedback, "white", attrs=["dark"]))
159
158
 
160
159
  followup_lines = [
161
160
  "Continue the previous task. Feedback from orchestrator:",
@@ -45,6 +45,9 @@ steps:
45
45
  - "replacement": For "replace", the text to replace with
46
46
  - "insertion": For "insert_after" and "insert_before", the text to insert
47
47
  2. "explanation": Brief explanation of the changes made
48
+
49
+ Example response:
50
+ {"modifications": [{"type": "replace", "original": "old code", "replacement": "new code"}], "explanation": "Updated the code"}
48
51
  """
49
52
 
50
53
  response = get_llm_response(prompt, model=npc.model, provider=npc.provider, npc=npc, format="json")