npcsh 1.1.20__py3-none-any.whl → 1.1.21__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. npcsh/_state.py +5 -71
  2. npcsh/diff_viewer.py +3 -3
  3. npcsh/npc_team/jinxs/lib/core/compress.jinx +373 -85
  4. npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +17 -6
  5. npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +17 -6
  6. npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +19 -8
  7. npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +52 -14
  8. npcsh/npc_team/jinxs/{bin → lib/utils}/benchmark.jinx +2 -2
  9. npcsh/npc_team/jinxs/{bin → lib/utils}/jinxs.jinx +12 -12
  10. npcsh/npc_team/jinxs/{bin → lib/utils}/models.jinx +7 -7
  11. npcsh/npc_team/jinxs/{bin → lib/utils}/setup.jinx +6 -6
  12. npcsh/npc_team/jinxs/modes/alicanto.jinx +1573 -296
  13. npcsh/npc_team/jinxs/modes/arxiv.jinx +5 -5
  14. npcsh/npc_team/jinxs/modes/config_tui.jinx +300 -0
  15. npcsh/npc_team/jinxs/modes/corca.jinx +3 -3
  16. npcsh/npc_team/jinxs/modes/git.jinx +795 -0
  17. {npcsh-1.1.20.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/modes}/kg.jinx +13 -13
  18. npcsh/npc_team/jinxs/modes/memories.jinx +414 -0
  19. npcsh/npc_team/jinxs/{bin → modes}/nql.jinx +10 -21
  20. npcsh/npc_team/jinxs/modes/papers.jinx +578 -0
  21. npcsh/npc_team/jinxs/modes/plonk.jinx +490 -304
  22. npcsh/npc_team/jinxs/modes/reattach.jinx +3 -3
  23. npcsh/npc_team/jinxs/modes/spool.jinx +3 -3
  24. npcsh/npc_team/jinxs/{bin → modes}/team.jinx +12 -12
  25. npcsh/npc_team/jinxs/modes/vixynt.jinx +388 -0
  26. npcsh/npc_team/jinxs/modes/wander.jinx +454 -181
  27. npcsh/npc_team/jinxs/modes/yap.jinx +10 -3
  28. npcsh/npcsh.py +112 -47
  29. npcsh/routes.py +4 -1
  30. npcsh/salmon_simulation.py +0 -0
  31. npcsh-1.1.21.data/data/npcsh/npc_team/alicanto.jinx +1633 -0
  32. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/arxiv.jinx +5 -5
  33. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/benchmark.jinx +2 -2
  34. npcsh-1.1.21.data/data/npcsh/npc_team/compress.jinx +428 -0
  35. npcsh-1.1.21.data/data/npcsh/npc_team/config_tui.jinx +300 -0
  36. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.jinx +3 -3
  37. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/db_search.jinx +17 -6
  38. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/file_search.jinx +17 -6
  39. npcsh-1.1.21.data/data/npcsh/npc_team/git.jinx +795 -0
  40. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/jinxs.jinx +12 -12
  41. {npcsh/npc_team/jinxs/bin → npcsh-1.1.21.data/data/npcsh/npc_team}/kg.jinx +13 -13
  42. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kg_search.jinx +19 -8
  43. npcsh-1.1.21.data/data/npcsh/npc_team/memories.jinx +414 -0
  44. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/models.jinx +7 -7
  45. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/nql.jinx +10 -21
  46. npcsh-1.1.21.data/data/npcsh/npc_team/papers.jinx +578 -0
  47. npcsh-1.1.21.data/data/npcsh/npc_team/plonk.jinx +565 -0
  48. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/reattach.jinx +3 -3
  49. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/setup.jinx +6 -6
  50. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/spool.jinx +3 -3
  51. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/team.jinx +12 -12
  52. npcsh-1.1.21.data/data/npcsh/npc_team/vixynt.jinx +388 -0
  53. npcsh-1.1.21.data/data/npcsh/npc_team/wander.jinx +728 -0
  54. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/web_search.jinx +52 -14
  55. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/yap.jinx +10 -3
  56. {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/METADATA +2 -2
  57. {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/RECORD +145 -150
  58. npcsh-1.1.21.dist-info/entry_points.txt +11 -0
  59. npcsh/npc_team/jinxs/bin/config_tui.jinx +0 -300
  60. npcsh/npc_team/jinxs/bin/memories.jinx +0 -317
  61. npcsh/npc_team/jinxs/bin/vixynt.jinx +0 -122
  62. npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +0 -73
  63. npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +0 -388
  64. npcsh/npc_team/jinxs/lib/research/paper_search.jinx +0 -412
  65. npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +0 -386
  66. npcsh/npc_team/plonkjr.npc +0 -23
  67. npcsh-1.1.20.data/data/npcsh/npc_team/alicanto.jinx +0 -356
  68. npcsh-1.1.20.data/data/npcsh/npc_team/compress.jinx +0 -140
  69. npcsh-1.1.20.data/data/npcsh/npc_team/config_tui.jinx +0 -300
  70. npcsh-1.1.20.data/data/npcsh/npc_team/mem_review.jinx +0 -73
  71. npcsh-1.1.20.data/data/npcsh/npc_team/mem_search.jinx +0 -388
  72. npcsh-1.1.20.data/data/npcsh/npc_team/memories.jinx +0 -317
  73. npcsh-1.1.20.data/data/npcsh/npc_team/paper_search.jinx +0 -412
  74. npcsh-1.1.20.data/data/npcsh/npc_team/plonk.jinx +0 -379
  75. npcsh-1.1.20.data/data/npcsh/npc_team/plonkjr.npc +0 -23
  76. npcsh-1.1.20.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -386
  77. npcsh-1.1.20.data/data/npcsh/npc_team/vixynt.jinx +0 -122
  78. npcsh-1.1.20.data/data/npcsh/npc_team/wander.jinx +0 -455
  79. npcsh-1.1.20.dist-info/entry_points.txt +0 -25
  80. /npcsh/npc_team/jinxs/lib/{orchestration → core}/convene.jinx +0 -0
  81. /npcsh/npc_team/jinxs/lib/{orchestration → core}/delegate.jinx +0 -0
  82. /npcsh/npc_team/jinxs/{bin → lib/core}/sample.jinx +0 -0
  83. /npcsh/npc_team/jinxs/{bin → lib/utils}/sync.jinx +0 -0
  84. /npcsh/npc_team/jinxs/{bin → modes}/roll.jinx +0 -0
  85. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/add_tab.jinx +0 -0
  86. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/alicanto.npc +0 -0
  87. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/alicanto.png +0 -0
  88. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
  89. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
  90. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/build.jinx +0 -0
  91. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/chat.jinx +0 -0
  92. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/click.jinx +0 -0
  93. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  94. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_pane.jinx +0 -0
  95. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_tab.jinx +0 -0
  96. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/cmd.jinx +0 -0
  97. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/compile.jinx +0 -0
  98. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/confirm.jinx +0 -0
  99. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/convene.jinx +0 -0
  100. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.npc +0 -0
  101. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.png +0 -0
  102. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca_example.png +0 -0
  103. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/delegate.jinx +0 -0
  104. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/edit_file.jinx +0 -0
  105. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/focus_pane.jinx +0 -0
  106. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/frederic.npc +0 -0
  107. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/frederic4.png +0 -0
  108. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.jinx +0 -0
  109. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.npc +0 -0
  110. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.png +0 -0
  111. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/help.jinx +0 -0
  112. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/incognide.jinx +0 -0
  113. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/init.jinx +0 -0
  114. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
  115. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  116. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/key_press.jinx +0 -0
  117. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
  118. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/list_panes.jinx +0 -0
  119. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  120. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/navigate.jinx +0 -0
  121. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/notify.jinx +0 -0
  122. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  123. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  124. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
  125. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/open_pane.jinx +0 -0
  126. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/ots.jinx +0 -0
  127. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/paste.jinx +0 -0
  128. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonk.npc +0 -0
  129. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonk.png +0 -0
  130. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  131. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/pti.jinx +0 -0
  132. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/python.jinx +0 -0
  133. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/read_pane.jinx +0 -0
  134. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/roll.jinx +0 -0
  135. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/run_terminal.jinx +0 -0
  136. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sample.jinx +0 -0
  137. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
  138. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/search.jinx +0 -0
  139. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/send_message.jinx +0 -0
  140. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/serve.jinx +0 -0
  141. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/set.jinx +0 -0
  142. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sh.jinx +0 -0
  143. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/shh.jinx +0 -0
  144. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sibiji.npc +0 -0
  145. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sibiji.png +0 -0
  146. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sleep.jinx +0 -0
  147. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/split_pane.jinx +0 -0
  148. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/spool.png +0 -0
  149. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sql.jinx +0 -0
  150. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch.jinx +0 -0
  151. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch_npc.jinx +0 -0
  152. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch_tab.jinx +0 -0
  153. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switches.jinx +0 -0
  154. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sync.jinx +0 -0
  155. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
  156. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/trigger.jinx +0 -0
  157. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/type_text.jinx +0 -0
  158. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/usage.jinx +0 -0
  159. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  160. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/wait.jinx +0 -0
  161. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/write_file.jinx +0 -0
  162. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/yap.png +0 -0
  163. {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/zen_mode.jinx +0 -0
  164. {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/WHEEL +0 -0
  165. {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/licenses/LICENSE +0 -0
  166. {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/top_level.txt +0 -0
@@ -1,356 +0,0 @@
1
- jinx_name: alicanto
2
- description: Deep research mode - multi-perspective exploration with gold insights and cliff warnings
3
- npc: forenpc
4
- inputs:
5
- - query: null
6
- - num_npcs: 5
7
- - depth: 3
8
- - model: null
9
- - provider: null
10
- - max_steps: 20
11
- - skip_research: true
12
- - exploration: 0.3
13
- - creativity: 0.5
14
- - format: report
15
- - browse: false
16
-
17
- steps:
18
- - name: alicanto_research
19
- engine: python
20
- code: |
21
- import os
22
- import sys
23
- import tty
24
- import termios
25
- from termcolor import colored
26
-
27
- from npcpy.llm_funcs import get_llm_response
28
- from npcpy.data.web import search_web
29
- from npcpy.npc_compiler import NPC
30
-
31
- npc = context.get('npc')
32
- team = context.get('team')
33
- messages = context.get('messages', [])
34
-
35
- # Resolve npc if it's a string (npc name) rather than NPC object
36
- if isinstance(npc, str) and team:
37
- npc = team.get(npc) if hasattr(team, 'get') else None
38
- elif isinstance(npc, str):
39
- npc = None # Can't use string npc without team to resolve it
40
-
41
- # ========== TUI Helper Functions ==========
42
- def get_terminal_size():
43
- try:
44
- size = os.get_terminal_size()
45
- return size.columns, size.lines
46
- except:
47
- return 80, 24
48
-
49
- def research_tui_browser(result):
50
- """Interactive TUI browser for research results"""
51
- perspectives = result.get('perspectives', '').split('\n')
52
- insights = result.get('insights', [])
53
- gold = result.get('gold', [])
54
- cliffs = result.get('cliffs', [])
55
- report = result.get('report', '')
56
-
57
- # Build tabs
58
- tabs = ['Gold', 'Cliffs', 'Insights', 'Report']
59
- current_tab = 0
60
-
61
- width, height = get_terminal_size()
62
- selected = 0
63
- scroll = 0
64
- list_height = height - 5
65
-
66
- fd = sys.stdin.fileno()
67
- old_settings = termios.tcgetattr(fd)
68
-
69
- try:
70
- tty.setcbreak(fd)
71
- sys.stdout.write('\033[?25l')
72
- sys.stdout.write('\033[2J\033[H')
73
-
74
- while True:
75
- width, height = get_terminal_size()
76
- list_height = height - 5
77
-
78
- # Get current content
79
- if current_tab == 0: # Gold
80
- items = gold if gold else ['No gold insights marked']
81
- elif current_tab == 1: # Cliffs
82
- items = cliffs if cliffs else ['No cliff warnings marked']
83
- elif current_tab == 2: # Insights
84
- items = [i[:200] for i in insights] if insights else ['No insights yet']
85
- else: # Report
86
- items = report.split('\n') if report else ['No report generated']
87
-
88
- if selected >= len(items):
89
- selected = max(0, len(items) - 1)
90
-
91
- if selected < scroll:
92
- scroll = selected
93
- elif selected >= scroll + list_height:
94
- scroll = selected - list_height + 1
95
-
96
- sys.stdout.write('\033[H')
97
-
98
- # Tab bar
99
- tab_bar = " "
100
- for i, tab in enumerate(tabs):
101
- if i == current_tab:
102
- tab_bar += f'\033[43;30;1m {tab} \033[0m '
103
- else:
104
- tab_bar += f'\033[90m {tab} \033[0m '
105
- sys.stdout.write(f'{tab_bar.ljust(width)}\n')
106
- sys.stdout.write(f'\033[90m{"─" * width}\033[0m\n')
107
-
108
- # Content
109
- for i in range(list_height):
110
- idx = scroll + i
111
- sys.stdout.write(f'\033[{3+i};1H\033[K')
112
- if idx >= len(items):
113
- continue
114
-
115
- line = str(items[idx])[:width-2]
116
- if current_tab in [0, 1, 2] and idx == selected:
117
- sys.stdout.write(f'\033[7;1m>{line.ljust(width-2)}\033[0m')
118
- else:
119
- # Color gold/cliff markers
120
- if '[GOLD]' in line:
121
- sys.stdout.write(f'\033[33m {line}\033[0m')
122
- elif '[CLIFF]' in line:
123
- sys.stdout.write(f'\033[31m {line}\033[0m')
124
- else:
125
- sys.stdout.write(f' {line}')
126
-
127
- # Status bar
128
- sys.stdout.write(f'\033[{height-2};1H\033[K\033[90m{"─" * width}\033[0m')
129
- counts = f"Gold: {len(gold)} | Cliffs: {len(cliffs)} | Insights: {len(insights)}"
130
- sys.stdout.write(f'\033[{height-1};1H\033[K {counts}'.ljust(width)[:width])
131
- sys.stdout.write(f'\033[{height};1H\033[K\033[43;30m h/l:Tabs j/k:Nav Enter:View q:Quit [{selected+1}/{len(items)}] \033[0m')
132
-
133
- sys.stdout.flush()
134
-
135
- c = sys.stdin.read(1)
136
-
137
- if c == '\x1b':
138
- c2 = sys.stdin.read(1)
139
- if c2 == '[':
140
- c3 = sys.stdin.read(1)
141
- if c3 == 'A' and selected > 0:
142
- selected -= 1
143
- elif c3 == 'B' and selected < len(items) - 1:
144
- selected += 1
145
- elif c3 == 'C': # Right
146
- current_tab = (current_tab + 1) % len(tabs)
147
- selected = 0
148
- scroll = 0
149
- elif c3 == 'D': # Left
150
- current_tab = (current_tab - 1) % len(tabs)
151
- selected = 0
152
- scroll = 0
153
- else:
154
- return
155
- continue
156
-
157
- if c == 'q' or c == '\x03':
158
- return
159
- elif c == 'k' and selected > 0:
160
- selected -= 1
161
- elif c == 'j' and selected < len(items) - 1:
162
- selected += 1
163
- elif c == 'h':
164
- current_tab = (current_tab - 1) % len(tabs)
165
- selected = 0
166
- scroll = 0
167
- elif c == 'l':
168
- current_tab = (current_tab + 1) % len(tabs)
169
- selected = 0
170
- scroll = 0
171
- elif c in ('\r', '\n') and current_tab < 3:
172
- # Show full item
173
- item = items[selected] if selected < len(items) else ''
174
- print(f'\033[2J\033[H{item}\n\nPress any key to continue...')
175
- sys.stdout.flush()
176
- sys.stdin.read(1)
177
- sys.stdout.write('\033[2J\033[H')
178
-
179
- finally:
180
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
181
- sys.stdout.write('\033[?25h')
182
- sys.stdout.write('\033[2J\033[H')
183
- sys.stdout.flush()
184
-
185
- query = context.get('query')
186
- num_npcs = int(context.get('num_npcs', 5))
187
- depth = int(context.get('depth', 3))
188
- max_steps = int(context.get('max_steps', 20))
189
- skip_research = context.get('skip_research', True)
190
- exploration = float(context.get('exploration', 0.3))
191
- creativity = float(context.get('creativity', 0.5))
192
- output_format = context.get('format', 'report')
193
-
194
- # Handle case where npc might be a string (npc name) or NPC object
195
- model = context.get('model') or (npc.model if npc and hasattr(npc, 'model') else 'gemini-1.5-pro')
196
- provider = context.get('provider') or (npc.provider if npc and hasattr(npc, 'provider') else 'gemini')
197
-
198
- if not query:
199
- context['output'] = """Usage: /alicanto <research query>
200
-
201
- Options:
202
- --num-npcs N Number of research perspectives (default: 5)
203
- --depth N Research depth (default: 3)
204
- --max-steps N Maximum research steps (default: 20)
205
- --exploration F Exploration factor 0-1 (default: 0.3)
206
- --creativity F Creativity factor 0-1 (default: 0.5)
207
- --format FORMAT Output: report|summary|full (default: report)
208
-
209
- Example: /alicanto What are the latest advances in quantum computing?"""
210
- context['messages'] = messages
211
- exit()
212
-
213
- print(f"""
214
- █████╗ ██╗ ██╗ ██████╗ █████╗ ███╗ ██╗████████╗ ██████╗
215
- ██╔══██╗██║ ██║██╔════╝██╔══██╗████╗ ██║╚══██╔══╝██╔═══██╗
216
- ███████║██║ ██║██║ ███████║██╔██╗ ██║ ██║ ██║ ██║
217
- ██╔══██║██║ ██║██║ ██╔══██║██║╚██╗██║ ██║ ██║ ██║
218
- ██║ ██║███████╗██║╚██████╗██║ ██║██║ ╚████║ ██║ ╚██████╔╝
219
- ╚═╝ ╚═╝╚══════╝╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═════╝
220
-
221
- Deep Research Mode
222
- Query: {query}
223
- Perspectives: {num_npcs} | Depth: {depth} | Max Steps: {max_steps}
224
- """)
225
-
226
- # Generate research perspectives
227
- perspectives_prompt = f"""Generate {num_npcs} distinct research perspectives for investigating: "{query}"
228
-
229
- For each perspective, provide:
230
- 1. Name (a descriptive title)
231
- 2. Approach (how this perspective would investigate)
232
- 3. Key questions to explore
233
-
234
- Return as a numbered list."""
235
-
236
- print(colored("Generating research perspectives...", "cyan"))
237
- resp = get_llm_response(
238
- perspectives_prompt,
239
- model=model,
240
- provider=provider,
241
- npc=npc
242
- )
243
- perspectives = str(resp.get('response', ''))
244
- print(perspectives)
245
-
246
- # Conduct web research if not skipped
247
- research_findings = ""
248
- if not skip_research:
249
- print(colored("\nConducting web research...", "cyan"))
250
- try:
251
- search_results = search_web(query, n_results=5)
252
- if search_results:
253
- research_findings = "\n\nWeb Research Findings:\n"
254
- for i, result in enumerate(search_results[:5], 1):
255
- title = result.get('title', 'No title')
256
- snippet = result.get('snippet', result.get('body', ''))[:200]
257
- research_findings += f"\n{i}. {title}\n {snippet}...\n"
258
- print(colored(f"Found {len(search_results)} sources", "green"))
259
- except Exception as e:
260
- print(colored(f"Web search error: {e}", "yellow"))
261
-
262
- # Multi-step exploration from each perspective
263
- all_insights = []
264
- gold_insights = [] # Key valuable findings
265
- cliff_warnings = [] # Potential pitfalls or caveats
266
-
267
- for step in range(min(depth, max_steps)):
268
- print(colored(f"\n--- Research Depth {step + 1}/{depth} ---", "cyan"))
269
-
270
- explore_prompt = f"""Research query: "{query}"
271
-
272
- Perspectives generated:
273
- {perspectives}
274
-
275
- {research_findings}
276
-
277
- Previous insights: {all_insights[-3:] if all_insights else 'None yet'}
278
-
279
- For depth level {step + 1}:
280
- 1. Explore deeper implications from each perspective
281
- 2. Identify GOLD insights (valuable, non-obvious findings) - mark with [GOLD]
282
- 3. Identify CLIFF warnings (pitfalls, caveats, risks) - mark with [CLIFF]
283
- 4. Connect insights across perspectives
284
-
285
- Exploration factor: {exploration} (higher = more diverse exploration)
286
- Creativity factor: {creativity} (higher = more novel connections)"""
287
-
288
- resp = get_llm_response(
289
- explore_prompt,
290
- model=model,
291
- provider=provider,
292
- temperature=creativity,
293
- npc=npc
294
- )
295
-
296
- step_insights = str(resp.get('response', ''))
297
- print(step_insights)
298
-
299
- # Extract gold and cliff markers
300
- if '[GOLD]' in step_insights:
301
- gold_insights.extend([line.strip() for line in step_insights.split('\n') if '[GOLD]' in line])
302
- if '[CLIFF]' in step_insights:
303
- cliff_warnings.extend([line.strip() for line in step_insights.split('\n') if '[CLIFF]' in line])
304
-
305
- all_insights.append(step_insights)
306
-
307
- # Generate final synthesis
308
- print(colored("\n--- Synthesizing Research ---", "cyan"))
309
-
310
- synthesis_prompt = f"""Synthesize research on: "{query}"
311
-
312
- All insights gathered:
313
- {chr(10).join(all_insights)}
314
-
315
- Gold insights identified:
316
- {chr(10).join(gold_insights) if gold_insights else 'None explicitly marked'}
317
-
318
- Cliff warnings identified:
319
- {chr(10).join(cliff_warnings) if cliff_warnings else 'None explicitly marked'}
320
-
321
- Generate a {output_format} that:
322
- 1. Summarizes key findings
323
- 2. Highlights the most valuable insights (gold)
324
- 3. Notes important caveats and risks (cliffs)
325
- 4. Provides actionable conclusions"""
326
-
327
- resp = get_llm_response(
328
- synthesis_prompt,
329
- model=model,
330
- provider=provider,
331
- npc=npc
332
- )
333
-
334
- final_report = str(resp.get('response', ''))
335
- print("\n" + "="*60)
336
- print(colored("ALICANTO RESEARCH REPORT", "green", attrs=['bold']))
337
- print("="*60)
338
- print(final_report)
339
-
340
- alicanto_result = {
341
- 'query': query,
342
- 'perspectives': perspectives,
343
- 'insights': all_insights,
344
- 'gold': gold_insights,
345
- 'cliffs': cliff_warnings,
346
- 'report': final_report
347
- }
348
-
349
- context['output'] = final_report
350
- context['messages'] = messages
351
- context['alicanto_result'] = alicanto_result
352
-
353
- # Launch interactive browser automatically
354
- if gold_insights or cliff_warnings or all_insights:
355
- print(colored("\nLaunching results browser...", "cyan"))
356
- research_tui_browser(alicanto_result)
@@ -1,140 +0,0 @@
1
- jinx_name: "compress"
2
- description: "Manages conversation and knowledge context. Defaults to compacting context. Use flags for other operations."
3
- inputs:
4
- - flush: ""
5
- - sleep: False
6
- - dream: False
7
- - ops: ""
8
- - model: ""
9
- - provider: ""
10
- steps:
11
- - name: "manage_context_and_memory"
12
- engine: "python"
13
- code: |
14
- import os
15
- import traceback
16
- from npcpy.llm_funcs import breathe
17
- from npcpy.memory.command_history import CommandHistory, load_kg_from_db, save_kg_to_db
18
- from npcpy.memory.knowledge_graph import kg_sleep_process, kg_dream_process
19
-
20
- # --- Get all inputs from context ---
21
- flush_n_str = context.get('flush')
22
- is_sleeping = context.get('sleep')
23
- is_dreaming = context.get('dream')
24
- operations_str = context.get('ops')
25
- llm_model = context.get('model')
26
- llm_provider = context.get('provider')
27
- output_messages = context.get('messages', [])
28
-
29
- USAGE = """Usage:
30
- /compress (Compacts conversation context)
31
- /compress --flush <number> (Removes the last N messages)
32
- /compress --sleep [...] (Evolves the knowledge graph)
33
- --dream (With --sleep: enables creative synthesis)
34
- --ops "op1,op2" (With --sleep: specifies KG operations)
35
- --model <name> (With --sleep: specifies LLM model)
36
- --provider <name> (With --sleep: specifies LLM provider)"""
37
-
38
- # --- Argument Validation: Ensure mutual exclusivity ---
39
- is_flushing = flush_n_str is not None and flush_n_str.strip() != ''
40
- if is_sleeping and is_flushing:
41
- context['output'] = f"Error: --sleep and --flush are mutually exclusive.\n{USAGE}"
42
- context['messages'] = output_messages
43
- exit()
44
-
45
- # --- Dispatcher: Route to the correct functionality ---
46
-
47
- # 1. SLEEP: Evolve the Knowledge Graph
48
- if is_sleeping:
49
- current_npc = context.get('npc')
50
- current_team = context.get('team')
51
-
52
- # Parameter setup for KG process
53
- operations_config = [op.strip() for op in operations_str.split(',')] if operations_str else None
54
- if not llm_model and current_npc: llm_model = current_npc.model
55
- if not llm_provider and current_npc: llm_provider = current_npc.provider
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
-
59
- team_name = current_team.name if current_team else "__none__"
60
- npc_name = current_npc.name if current_npc else "__none__"
61
- current_path = os.getcwd()
62
- scope_str = f"Team: '{team_name}', NPC: '{npc_name}', Path: '{current_path}'"
63
-
64
- command_history = None
65
- try:
66
- db_path = os.getenv("NPCSH_DB_PATH", os.path.expanduser("~/npcsh_history.db"))
67
- command_history = CommandHistory(db_path)
68
- engine = command_history.engine
69
- current_kg = load_kg_from_db(engine, team_name, npc_name, current_path)
70
-
71
- if not current_kg or not current_kg.get('facts'):
72
- context['output'] = f"Knowledge graph for the current scope is empty. Nothing to process.\n- Scope: {scope_str}"
73
- exit()
74
-
75
- original_facts = len(current_kg.get('facts', []))
76
- original_concepts = len(current_kg.get('concepts', []))
77
-
78
- evolved_kg, _ = kg_sleep_process(existing_kg=current_kg, model=llm_model, provider=llm_provider, npc=current_npc, operations_config=operations_config)
79
- process_type = "Sleep"
80
-
81
- if is_dreaming:
82
- evolved_kg, _ = kg_dream_process(existing_kg=evolved_kg, model=llm_model, provider=llm_provider, npc=current_npc)
83
- process_type += " & Dream"
84
-
85
- save_kg_to_db(engine, evolved_kg, team_name, npc_name, current_path)
86
-
87
- new_facts = len(evolved_kg.get('facts', []))
88
- new_concepts = len(evolved_kg.get('concepts', []))
89
-
90
- context['output'] = (f"{process_type} process complete.\n"
91
- f"- Facts: {original_facts} -> {new_facts} ({new_facts - original_facts:+})\n"
92
- f"- Concepts: {original_concepts} -> {new_concepts} ({new_concepts - original_concepts:+})")
93
- except Exception as e:
94
- traceback.print_exc()
95
- context['output'] = f"Error during KG evolution: {e}"
96
- finally:
97
- if command_history: command_history.close()
98
- context['messages'] = output_messages
99
-
100
- # 2. FLUSH: Remove messages from context
101
- elif is_flushing:
102
- try:
103
- n = int(flush_n_str)
104
- if n <= 0:
105
- context['output'] = "Error: Number of messages to flush must be positive."
106
- exit()
107
- except ValueError:
108
- context['output'] = f"Error: Invalid number '{flush_n_str}'. {USAGE}"
109
- exit()
110
-
111
- messages_list = list(output_messages)
112
- original_len = len(messages_list)
113
- final_messages = []
114
-
115
- if messages_list and messages_list[0].get("role") == "system":
116
- system_message = messages_list.pop(0)
117
- num_to_remove = min(n, len(messages_list))
118
- final_messages = [system_message] + messages_list[:-num_to_remove]
119
- else:
120
- num_to_remove = min(n, original_len)
121
- final_messages = messages_list[:-num_to_remove]
122
-
123
- removed_count = original_len - len(final_messages)
124
- context['output'] = f"Flushed {removed_count} message(s). Context is now {len(final_messages)} messages."
125
- context['messages'] = final_messages
126
-
127
- # 3. DEFAULT: Compact conversation context
128
- else:
129
- try:
130
- result = breathe(**context)
131
- if isinstance(result, dict):
132
- context['output'] = result.get('output', 'Context compressed.')
133
- context['messages'] = result.get('messages', output_messages)
134
- else:
135
- context['output'] = "Context compression process initiated."
136
- context['messages'] = output_messages
137
- except Exception as e:
138
- traceback.print_exc()
139
- context['output'] = f"Error during context compression: {e}"
140
- context['messages'] = output_messages