npcsh 1.1.16__py3-none-any.whl → 1.1.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. npcsh/_state.py +138 -100
  2. npcsh/alicanto.py +2 -2
  3. npcsh/benchmark/__init__.py +28 -0
  4. npcsh/benchmark/npcsh_agent.py +296 -0
  5. npcsh/benchmark/runner.py +611 -0
  6. npcsh/benchmark/templates/install-npcsh.sh.j2 +35 -0
  7. npcsh/build.py +2 -4
  8. npcsh/completion.py +2 -6
  9. npcsh/config.py +1 -3
  10. npcsh/conversation_viewer.py +389 -0
  11. npcsh/corca.py +0 -1
  12. npcsh/execution.py +0 -1
  13. npcsh/guac.py +0 -1
  14. npcsh/mcp_helpers.py +2 -3
  15. npcsh/mcp_server.py +5 -10
  16. npcsh/npc.py +10 -11
  17. npcsh/npc_team/jinxs/bin/benchmark.jinx +146 -0
  18. npcsh/npc_team/jinxs/bin/nql.jinx +7 -7
  19. npcsh/npc_team/jinxs/bin/roll.jinx +20 -23
  20. npcsh/npc_team/jinxs/bin/sample.jinx +6 -7
  21. npcsh/npc_team/jinxs/bin/sync.jinx +6 -6
  22. npcsh/npc_team/jinxs/bin/vixynt.jinx +8 -8
  23. npcsh/npc_team/jinxs/incognide/add_tab.jinx +11 -0
  24. npcsh/npc_team/jinxs/incognide/close_pane.jinx +9 -0
  25. npcsh/npc_team/jinxs/incognide/close_tab.jinx +10 -0
  26. npcsh/npc_team/jinxs/incognide/confirm.jinx +10 -0
  27. npcsh/npc_team/jinxs/incognide/focus_pane.jinx +9 -0
  28. npcsh/npc_team/jinxs/{npc_studio/npc-studio.jinx → incognide/incognide.jinx} +2 -2
  29. npcsh/npc_team/jinxs/incognide/list_panes.jinx +8 -0
  30. npcsh/npc_team/jinxs/incognide/navigate.jinx +10 -0
  31. npcsh/npc_team/jinxs/incognide/notify.jinx +10 -0
  32. npcsh/npc_team/jinxs/incognide/open_pane.jinx +13 -0
  33. npcsh/npc_team/jinxs/incognide/read_pane.jinx +9 -0
  34. npcsh/npc_team/jinxs/incognide/run_terminal.jinx +10 -0
  35. npcsh/npc_team/jinxs/incognide/send_message.jinx +10 -0
  36. npcsh/npc_team/jinxs/incognide/split_pane.jinx +12 -0
  37. npcsh/npc_team/jinxs/incognide/switch_npc.jinx +10 -0
  38. npcsh/npc_team/jinxs/incognide/switch_tab.jinx +10 -0
  39. npcsh/npc_team/jinxs/incognide/write_file.jinx +11 -0
  40. npcsh/npc_team/jinxs/incognide/zen_mode.jinx +9 -0
  41. npcsh/npc_team/jinxs/lib/browser/browser_action.jinx +4 -4
  42. npcsh/npc_team/jinxs/lib/browser/browser_screenshot.jinx +1 -1
  43. npcsh/npc_team/jinxs/lib/browser/open_browser.jinx +2 -2
  44. npcsh/npc_team/jinxs/lib/computer_use/click.jinx +2 -2
  45. npcsh/npc_team/jinxs/lib/computer_use/key_press.jinx +1 -1
  46. npcsh/npc_team/jinxs/lib/computer_use/launch_app.jinx +1 -1
  47. npcsh/npc_team/jinxs/lib/computer_use/screenshot.jinx +1 -1
  48. npcsh/npc_team/jinxs/lib/computer_use/trigger.jinx +2 -2
  49. npcsh/npc_team/jinxs/lib/computer_use/type_text.jinx +1 -1
  50. npcsh/npc_team/jinxs/lib/computer_use/wait.jinx +1 -1
  51. npcsh/npc_team/jinxs/lib/core/chat.jinx +4 -4
  52. npcsh/npc_team/jinxs/lib/core/cmd.jinx +4 -4
  53. npcsh/npc_team/jinxs/lib/core/compress.jinx +8 -8
  54. npcsh/npc_team/jinxs/lib/core/edit_file.jinx +3 -0
  55. npcsh/npc_team/jinxs/lib/core/ots.jinx +7 -7
  56. npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +348 -0
  57. npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +339 -0
  58. npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +418 -0
  59. npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +73 -0
  60. npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +388 -0
  61. npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +283 -0
  62. npcsh/npc_team/jinxs/lib/core/search.jinx +52 -129
  63. npcsh/npc_team/jinxs/lib/core/sh.jinx +1 -1
  64. npcsh/npc_team/jinxs/lib/core/sleep.jinx +29 -18
  65. npcsh/npc_team/jinxs/lib/core/sql.jinx +15 -11
  66. npcsh/npc_team/jinxs/lib/orchestration/convene.jinx +7 -7
  67. npcsh/npc_team/jinxs/lib/orchestration/delegate.jinx +8 -9
  68. npcsh/npc_team/jinxs/lib/research/paper_search.jinx +389 -78
  69. npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +373 -56
  70. npcsh/npc_team/jinxs/lib/utils/build.jinx +5 -5
  71. npcsh/npc_team/jinxs/lib/utils/compile.jinx +2 -2
  72. npcsh/npc_team/jinxs/lib/utils/help.jinx +1 -1
  73. npcsh/npc_team/jinxs/lib/utils/init.jinx +5 -5
  74. npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +300 -145
  75. npcsh/npc_team/jinxs/lib/utils/serve.jinx +2 -2
  76. npcsh/npc_team/jinxs/lib/utils/set.jinx +2 -2
  77. npcsh/npc_team/jinxs/lib/utils/switch.jinx +3 -3
  78. npcsh/npc_team/jinxs/lib/utils/switches.jinx +1 -1
  79. npcsh/npc_team/jinxs/lib/utils/teamviz.jinx +2 -2
  80. npcsh/npc_team/jinxs/modes/alicanto.jinx +356 -0
  81. npcsh/npc_team/jinxs/modes/arxiv.jinx +720 -0
  82. npcsh/npc_team/jinxs/modes/corca.jinx +430 -0
  83. npcsh/npc_team/jinxs/modes/guac.jinx +544 -0
  84. npcsh/npc_team/jinxs/modes/plonk.jinx +379 -0
  85. npcsh/npc_team/jinxs/modes/pti.jinx +357 -0
  86. npcsh/npc_team/jinxs/modes/reattach.jinx +291 -0
  87. npcsh/npc_team/jinxs/modes/spool.jinx +350 -0
  88. npcsh/npc_team/jinxs/modes/wander.jinx +455 -0
  89. {npcsh-1.1.16.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/modes}/yap.jinx +8 -2
  90. npcsh/npc_team/sibiji.npc +1 -1
  91. npcsh/npcsh.py +87 -46
  92. npcsh/plonk.py +0 -1
  93. npcsh/pti.py +0 -1
  94. npcsh/routes.py +1 -3
  95. npcsh/spool.py +0 -1
  96. npcsh/ui.py +0 -1
  97. npcsh/wander.py +0 -1
  98. npcsh/yap.py +0 -1
  99. npcsh-1.1.18.data/data/npcsh/npc_team/add_tab.jinx +11 -0
  100. npcsh-1.1.18.data/data/npcsh/npc_team/alicanto.jinx +356 -0
  101. npcsh-1.1.18.data/data/npcsh/npc_team/arxiv.jinx +720 -0
  102. npcsh-1.1.18.data/data/npcsh/npc_team/benchmark.jinx +146 -0
  103. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/browser_action.jinx +4 -4
  104. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/browser_screenshot.jinx +1 -1
  105. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/build.jinx +5 -5
  106. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/chat.jinx +4 -4
  107. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/click.jinx +2 -2
  108. npcsh-1.1.18.data/data/npcsh/npc_team/close_pane.jinx +9 -0
  109. npcsh-1.1.18.data/data/npcsh/npc_team/close_tab.jinx +10 -0
  110. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/cmd.jinx +4 -4
  111. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/compile.jinx +2 -2
  112. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/compress.jinx +8 -8
  113. npcsh-1.1.18.data/data/npcsh/npc_team/confirm.jinx +10 -0
  114. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/convene.jinx +7 -7
  115. npcsh-1.1.18.data/data/npcsh/npc_team/corca.jinx +430 -0
  116. npcsh-1.1.18.data/data/npcsh/npc_team/db_search.jinx +348 -0
  117. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/delegate.jinx +8 -9
  118. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/edit_file.jinx +3 -0
  119. npcsh-1.1.18.data/data/npcsh/npc_team/file_search.jinx +339 -0
  120. npcsh-1.1.18.data/data/npcsh/npc_team/focus_pane.jinx +9 -0
  121. npcsh-1.1.18.data/data/npcsh/npc_team/guac.jinx +544 -0
  122. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/help.jinx +1 -1
  123. npcsh-1.1.16.data/data/npcsh/npc_team/npc-studio.jinx → npcsh-1.1.18.data/data/npcsh/npc_team/incognide.jinx +2 -2
  124. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/init.jinx +5 -5
  125. npcsh-1.1.18.data/data/npcsh/npc_team/jinxs.jinx +331 -0
  126. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/key_press.jinx +1 -1
  127. npcsh-1.1.18.data/data/npcsh/npc_team/kg_search.jinx +418 -0
  128. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/launch_app.jinx +1 -1
  129. npcsh-1.1.18.data/data/npcsh/npc_team/list_panes.jinx +8 -0
  130. npcsh-1.1.18.data/data/npcsh/npc_team/mem_review.jinx +73 -0
  131. npcsh-1.1.18.data/data/npcsh/npc_team/mem_search.jinx +388 -0
  132. npcsh-1.1.18.data/data/npcsh/npc_team/navigate.jinx +10 -0
  133. npcsh-1.1.18.data/data/npcsh/npc_team/notify.jinx +10 -0
  134. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/nql.jinx +7 -7
  135. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/open_browser.jinx +2 -2
  136. npcsh-1.1.18.data/data/npcsh/npc_team/open_pane.jinx +13 -0
  137. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/ots.jinx +7 -7
  138. npcsh-1.1.18.data/data/npcsh/npc_team/paper_search.jinx +412 -0
  139. npcsh-1.1.18.data/data/npcsh/npc_team/plonk.jinx +379 -0
  140. npcsh-1.1.18.data/data/npcsh/npc_team/pti.jinx +357 -0
  141. npcsh-1.1.18.data/data/npcsh/npc_team/read_pane.jinx +9 -0
  142. npcsh-1.1.18.data/data/npcsh/npc_team/reattach.jinx +291 -0
  143. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/roll.jinx +20 -23
  144. npcsh-1.1.18.data/data/npcsh/npc_team/run_terminal.jinx +10 -0
  145. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sample.jinx +6 -7
  146. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/screenshot.jinx +1 -1
  147. npcsh-1.1.18.data/data/npcsh/npc_team/search.jinx +54 -0
  148. npcsh-1.1.18.data/data/npcsh/npc_team/semantic_scholar.jinx +386 -0
  149. npcsh-1.1.18.data/data/npcsh/npc_team/send_message.jinx +10 -0
  150. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/serve.jinx +2 -2
  151. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/set.jinx +2 -2
  152. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sh.jinx +1 -1
  153. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sibiji.npc +1 -1
  154. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sleep.jinx +29 -18
  155. npcsh-1.1.18.data/data/npcsh/npc_team/split_pane.jinx +12 -0
  156. npcsh-1.1.18.data/data/npcsh/npc_team/spool.jinx +350 -0
  157. npcsh-1.1.18.data/data/npcsh/npc_team/sql.jinx +20 -0
  158. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/switch.jinx +3 -3
  159. npcsh-1.1.18.data/data/npcsh/npc_team/switch_npc.jinx +10 -0
  160. npcsh-1.1.18.data/data/npcsh/npc_team/switch_tab.jinx +10 -0
  161. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/switches.jinx +1 -1
  162. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sync.jinx +6 -6
  163. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/teamviz.jinx +2 -2
  164. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/trigger.jinx +2 -2
  165. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/type_text.jinx +1 -1
  166. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/vixynt.jinx +8 -8
  167. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/wait.jinx +1 -1
  168. npcsh-1.1.18.data/data/npcsh/npc_team/wander.jinx +455 -0
  169. npcsh-1.1.18.data/data/npcsh/npc_team/web_search.jinx +283 -0
  170. npcsh-1.1.18.data/data/npcsh/npc_team/write_file.jinx +11 -0
  171. {npcsh/npc_team/jinxs/bin → npcsh-1.1.18.data/data/npcsh/npc_team}/yap.jinx +8 -2
  172. npcsh-1.1.18.data/data/npcsh/npc_team/zen_mode.jinx +9 -0
  173. {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/METADATA +99 -7
  174. npcsh-1.1.18.dist-info/RECORD +235 -0
  175. {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/WHEEL +1 -1
  176. {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/entry_points.txt +2 -3
  177. npcsh/npc_team/jinxs/bin/spool.jinx +0 -161
  178. npcsh/npc_team/jinxs/bin/wander.jinx +0 -152
  179. npcsh/npc_team/jinxs/lib/research/arxiv.jinx +0 -76
  180. npcsh-1.1.16.data/data/npcsh/npc_team/arxiv.jinx +0 -76
  181. npcsh-1.1.16.data/data/npcsh/npc_team/jinxs.jinx +0 -176
  182. npcsh-1.1.16.data/data/npcsh/npc_team/paper_search.jinx +0 -101
  183. npcsh-1.1.16.data/data/npcsh/npc_team/search.jinx +0 -131
  184. npcsh-1.1.16.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -69
  185. npcsh-1.1.16.data/data/npcsh/npc_team/spool.jinx +0 -161
  186. npcsh-1.1.16.data/data/npcsh/npc_team/sql.jinx +0 -16
  187. npcsh-1.1.16.data/data/npcsh/npc_team/wander.jinx +0 -152
  188. npcsh-1.1.16.dist-info/RECORD +0 -170
  189. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/alicanto.npc +0 -0
  190. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/alicanto.png +0 -0
  191. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  192. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/corca.npc +0 -0
  193. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/corca.png +0 -0
  194. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/corca_example.png +0 -0
  195. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/frederic.npc +0 -0
  196. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/frederic4.png +0 -0
  197. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/guac.npc +0 -0
  198. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/guac.png +0 -0
  199. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
  200. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  201. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  202. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  203. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  204. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/paste.jinx +0 -0
  205. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonk.npc +0 -0
  206. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonk.png +0 -0
  207. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonkjr.npc +0 -0
  208. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  209. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/python.jinx +0 -0
  210. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/shh.jinx +0 -0
  211. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sibiji.png +0 -0
  212. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/spool.png +0 -0
  213. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/usage.jinx +0 -0
  214. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  215. {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/yap.png +0 -0
  216. {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/licenses/LICENSE +0 -0
  217. {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,348 @@
1
+ jinx_name: db_search
2
+ description: Search conversation history database with interactive TUI
3
+ inputs:
4
+ - query: ""
5
+ - path: ""
6
+ - limit: "100"
7
+ - text: "false"
8
+
9
+ steps:
10
+ - name: search_db
11
+ engine: python
12
+ code: |
13
+ import os
14
+ import sys
15
+ import tty
16
+ import termios
17
+ from datetime import datetime
18
+ from sqlalchemy import create_engine, text
19
+
20
+ query = context.get('query', '').strip()
21
+ text_mode = context.get('text', '').lower() in ('true', '1', 'yes')
22
+
23
+ if not query:
24
+ lines = [
25
+ "Usage: /db_search <query>",
26
+ "",
27
+ "Searches conversation history for matching content.",
28
+ "",
29
+ "Options:",
30
+ " path - Filter by directory path",
31
+ " limit - Max results (default 100)",
32
+ " text - Text-only output, no TUI (true/false)",
33
+ "",
34
+ "TUI Controls:",
35
+ " j/k or arrows - Navigate",
36
+ " 1/2/3 - Sort by time/role/npc",
37
+ " p - Preview full message",
38
+ " r - Reattach to conversation",
39
+ " f - Filter by role (user/assistant/all)",
40
+ " q/ESC - Quit",
41
+ "",
42
+ "Examples:",
43
+ " /db_search python debugging",
44
+ " /db_search api errors path=/home/user/project",
45
+ " /db_search errors text=true",
46
+ ]
47
+ context['output'] = "\n".join(lines)
48
+ else:
49
+ db_path = os.getenv("NPCSH_DB_PATH", os.path.expanduser("~/npcsh_history.db"))
50
+ limit = int(context.get('limit') or 100)
51
+ filter_path = context.get('path', '').strip()
52
+
53
+ engine = create_engine(f'sqlite:///{db_path}')
54
+
55
+ try:
56
+ with engine.connect() as conn:
57
+ if filter_path:
58
+ result = conn.execute(text("""
59
+ SELECT conversation_id, timestamp, role, content, npc, directory_path
60
+ FROM conversation_history
61
+ WHERE LOWER(content) LIKE LOWER(:query)
62
+ AND (directory_path = :path OR directory_path LIKE :path_pattern)
63
+ ORDER BY timestamp DESC
64
+ LIMIT :limit
65
+ """), {
66
+ "query": f"%{query}%",
67
+ "path": filter_path,
68
+ "path_pattern": filter_path + "/%",
69
+ "limit": limit
70
+ })
71
+ else:
72
+ result = conn.execute(text("""
73
+ SELECT conversation_id, timestamp, role, content, npc, directory_path
74
+ FROM conversation_history
75
+ WHERE LOWER(content) LIKE LOWER(:query)
76
+ ORDER BY timestamp DESC
77
+ LIMIT :limit
78
+ """), {"query": f"%{query}%", "limit": limit})
79
+
80
+ rows = [dict(row._mapping) for row in result.fetchall()]
81
+
82
+ if not rows:
83
+ context['output'] = f"No results found for '{query}'"
84
+ elif text_mode:
85
+ # Text-only output
86
+ lines = [f"Found {len(rows)} results for '{query}':", ""]
87
+ for row in rows:
88
+ cid = row['conversation_id'][:8] if row['conversation_id'] else '?'
89
+ ts = str(row['timestamp'])[:16] if row['timestamp'] else '?'
90
+ role = row['role'] or '?'
91
+ content = (row['content'] or '')[:100].replace('\n', ' ')
92
+ npc_name = row['npc'] or 'default'
93
+ path = row['directory_path'] or ''
94
+
95
+ lines.append(f"[{cid}] {ts} ({role}/{npc_name})")
96
+ lines.append(f" {content}")
97
+ if path:
98
+ lines.append(f" @ {path}")
99
+ lines.append("")
100
+ context['output'] = "\n".join(lines)
101
+ else:
102
+ # Interactive TUI mode
103
+ def get_terminal_size():
104
+ try:
105
+ size = os.get_terminal_size()
106
+ return size.columns, size.lines
107
+ except:
108
+ return 80, 24
109
+
110
+ def format_ts(ts):
111
+ if not ts:
112
+ return 'unknown'
113
+ try:
114
+ if 'T' in str(ts):
115
+ dt = datetime.fromisoformat(str(ts).replace('Z', '+00:00'))
116
+ else:
117
+ dt = datetime.strptime(str(ts)[:19], '%Y-%m-%d %H:%M:%S')
118
+ now = datetime.now()
119
+ diff = now - dt.replace(tzinfo=None)
120
+ if diff.days == 0:
121
+ return f"Today {dt.strftime('%H:%M')}"
122
+ elif diff.days == 1:
123
+ return f"Yesterday {dt.strftime('%H:%M')}"
124
+ elif diff.days < 7:
125
+ return dt.strftime('%a %H:%M')
126
+ else:
127
+ return dt.strftime('%b %d %H:%M')
128
+ except:
129
+ return str(ts)[:16]
130
+
131
+ width, height = get_terminal_size()
132
+ selected = 0
133
+ scroll = 0
134
+ list_height = height - 5
135
+ mode = 'list'
136
+ preview_scroll = 0
137
+ sort_mode = 'time' # time, role, npc
138
+ role_filter = 'all' # all, user, assistant
139
+
140
+ def sort_rows(rows, sort_mode):
141
+ if sort_mode == 'time':
142
+ return sorted(rows, key=lambda x: x.get('timestamp') or '', reverse=True)
143
+ elif sort_mode == 'role':
144
+ return sorted(rows, key=lambda x: (x.get('role') or '', x.get('timestamp') or ''), reverse=True)
145
+ elif sort_mode == 'npc':
146
+ return sorted(rows, key=lambda x: (x.get('npc') or '', x.get('timestamp') or ''), reverse=True)
147
+ return rows
148
+
149
+ def filter_rows(rows, role_filter):
150
+ if role_filter == 'all':
151
+ return rows
152
+ return [r for r in rows if r.get('role') == role_filter]
153
+
154
+ display_rows = filter_rows(sort_rows(rows, sort_mode), role_filter)
155
+
156
+ fd = sys.stdin.fileno()
157
+ old_settings = termios.tcgetattr(fd)
158
+
159
+ try:
160
+ tty.setcbreak(fd)
161
+ sys.stdout.write('\033[?25l')
162
+ sys.stdout.write('\033[2J\033[H')
163
+
164
+ while True:
165
+ width, height = get_terminal_size()
166
+ list_height = height - 5
167
+
168
+ if mode == 'list':
169
+ if selected < scroll:
170
+ scroll = selected
171
+ elif selected >= scroll + list_height:
172
+ scroll = selected - list_height + 1
173
+
174
+ sys.stdout.write('\033[H')
175
+
176
+ # Header
177
+ if mode == 'list':
178
+ sort_ind = {'time': '1', 'role': '2', 'npc': '3'}[sort_mode]
179
+ header = f" DB SEARCH ({len(display_rows)} results): '{query}' [sort:{sort_mode}({sort_ind}) filter:{role_filter}] "
180
+ else:
181
+ 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
+
184
+ # Column headers
185
+ if mode == 'list':
186
+ col_header = f' {"TIMESTAMP":<16} {"ROLE":<10} {"NPC":<12} {"CONTENT":<40}'
187
+ sys.stdout.write(f'\033[90m{col_header[:width]}\033[0m\n')
188
+ else:
189
+ sys.stdout.write(f'\033[90m{"─" * width}\033[0m\n')
190
+
191
+ if mode == 'list':
192
+ for i in range(list_height):
193
+ idx = scroll + i
194
+ sys.stdout.write(f'\033[{3+i};1H\033[K')
195
+ if idx >= len(display_rows):
196
+ continue
197
+
198
+ r = display_rows[idx]
199
+ ts = format_ts(r.get('timestamp'))
200
+ role = (r.get('role') or '?')[:10]
201
+ npc_name = (r.get('npc') or 'default')[:12]
202
+ content = (r.get('content') or '')[:60].replace('\n', ' ')
203
+
204
+ # Color by role
205
+ if r.get('role') == 'user':
206
+ role_color = '\033[32m' # green
207
+ elif r.get('role') == 'assistant':
208
+ role_color = '\033[34m' # blue
209
+ else:
210
+ role_color = '\033[90m' # gray
211
+
212
+ line = f" {ts:<16} {role_color}{role:<10}\033[0m {npc_name:<12} {content}"
213
+ line = line[:width+20] # allow for color codes
214
+
215
+ if idx == selected:
216
+ sys.stdout.write(f'\033[47;30;1m>{line}\033[0m')
217
+ else:
218
+ sys.stdout.write(f' {line}')
219
+
220
+ # Status bar
221
+ sys.stdout.write(f'\033[{height-2};1H\033[K\033[90m{"─" * width}\033[0m')
222
+ sel = display_rows[selected] if display_rows else {}
223
+ cid = sel.get('conversation_id', '')[:20]
224
+ path = sel.get('directory_path', '')
225
+ if len(path) > 40:
226
+ path = '...' + path[-37:]
227
+ 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
+
230
+ else: # preview mode
231
+ sel = display_rows[selected]
232
+ content = sel.get('content') or ''
233
+ lines = content.split('\n')
234
+
235
+ for i in range(list_height):
236
+ idx = preview_scroll + i
237
+ sys.stdout.write(f'\033[{3+i};1H\033[K')
238
+ if idx < len(lines):
239
+ sys.stdout.write(lines[idx][:width-1])
240
+
241
+ sys.stdout.write(f'\033[{height-2};1H\033[K\033[90m{"─" * width}\033[0m')
242
+ role = sel.get('role') or '?'
243
+ npc_name = sel.get('npc') or 'default'
244
+ ts = format_ts(sel.get('timestamp'))
245
+ 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
+
248
+ sys.stdout.flush()
249
+
250
+ c = sys.stdin.read(1)
251
+
252
+ if c == '\x1b':
253
+ c2 = sys.stdin.read(1)
254
+ if c2 == '[':
255
+ c3 = sys.stdin.read(1)
256
+ if c3 == 'A': # Up
257
+ if mode == 'list' and selected > 0:
258
+ selected -= 1
259
+ elif mode == 'preview' and preview_scroll > 0:
260
+ preview_scroll -= 1
261
+ elif c3 == 'B': # Down
262
+ if mode == 'list' and selected < len(display_rows) - 1:
263
+ selected += 1
264
+ elif mode == 'preview':
265
+ sel = display_rows[selected]
266
+ lines = (sel.get('content') or '').split('\n')
267
+ if preview_scroll < max(0, len(lines) - list_height):
268
+ preview_scroll += 1
269
+ else:
270
+ if mode == 'preview':
271
+ mode = 'list'
272
+ sys.stdout.write('\033[2J\033[H')
273
+ else:
274
+ context['output'] = "Cancelled."
275
+ break
276
+ continue
277
+
278
+ if c == 'q' or c == '\x03':
279
+ context['output'] = "Cancelled."
280
+ break
281
+ elif c == 'k':
282
+ if mode == 'list' and selected > 0:
283
+ selected -= 1
284
+ elif mode == 'preview' and preview_scroll > 0:
285
+ preview_scroll -= 1
286
+ elif c == 'j':
287
+ if mode == 'list' and selected < len(display_rows) - 1:
288
+ selected += 1
289
+ elif mode == 'preview':
290
+ sel = display_rows[selected]
291
+ lines = (sel.get('content') or '').split('\n')
292
+ if preview_scroll < max(0, len(lines) - list_height):
293
+ preview_scroll += 1
294
+ elif c == '1':
295
+ sort_mode = 'time'
296
+ display_rows = filter_rows(sort_rows(rows, sort_mode), role_filter)
297
+ selected = 0
298
+ scroll = 0
299
+ elif c == '2':
300
+ sort_mode = 'role'
301
+ display_rows = filter_rows(sort_rows(rows, sort_mode), role_filter)
302
+ selected = 0
303
+ scroll = 0
304
+ elif c == '3':
305
+ sort_mode = 'npc'
306
+ display_rows = filter_rows(sort_rows(rows, sort_mode), role_filter)
307
+ selected = 0
308
+ scroll = 0
309
+ elif c == 'f' and mode == 'list':
310
+ # Cycle through filters
311
+ if role_filter == 'all':
312
+ role_filter = 'user'
313
+ elif role_filter == 'user':
314
+ role_filter = 'assistant'
315
+ else:
316
+ role_filter = 'all'
317
+ display_rows = filter_rows(sort_rows(rows, sort_mode), role_filter)
318
+ selected = 0
319
+ scroll = 0
320
+ elif c == 'p' and mode == 'list' and display_rows:
321
+ mode = 'preview'
322
+ preview_scroll = 0
323
+ sys.stdout.write('\033[2J\033[H')
324
+ elif c == 'b' and mode == 'preview':
325
+ mode = 'list'
326
+ sys.stdout.write('\033[2J\033[H')
327
+ elif c == 'r' and display_rows:
328
+ cid = display_rows[selected]['conversation_id']
329
+ if 'state' in dir() and state is not None:
330
+ state.conversation_id = cid
331
+ context['output'] = f"Reattached to: {cid}"
332
+ else:
333
+ context['output'] = f"Selected: {cid}\n\nRun: /set conversation_id={cid}"
334
+ break
335
+ elif c in ('\r', '\n') and display_rows:
336
+ cid = display_rows[selected]['conversation_id']
337
+ context['output'] = f"Selected: {cid}\n\nRun: /set conversation_id={cid}"
338
+ break
339
+
340
+ finally:
341
+ termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
342
+ sys.stdout.write('\033[?25h')
343
+ sys.stdout.write('\033[2J\033[H')
344
+ sys.stdout.flush()
345
+
346
+ except Exception as e:
347
+ import traceback
348
+ context['output'] = f"Search error: {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")