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
@@ -29,7 +29,7 @@ steps:
29
29
  from gtts import gTTS
30
30
  from npcpy.data.audio import (
31
31
  FORMAT, CHANNELS, RATE, CHUNK,
32
- transcribe_recording, convert_mp3_to_wav, cleanup_temp_files
32
+ transcribe_recording, convert_mp3_to_wav
33
33
  )
34
34
  AUDIO_AVAILABLE = True
35
35
  except ImportError as e:
@@ -144,7 +144,11 @@ steps:
144
144
  import winsound
145
145
  winsound.PlaySound(wav_path, winsound.SND_FILENAME)
146
146
 
147
- cleanup_temp_files([f.name, wav_path])
147
+ for _p in [f.name, wav_path]:
148
+ try:
149
+ os.remove(_p)
150
+ except:
151
+ pass
148
152
  except Exception as e:
149
153
  print(colored(f"TTS error: {e}", "red"))
150
154
 
@@ -189,7 +193,10 @@ steps:
189
193
  try:
190
194
  segments, _ = whisper_model.transcribe(audio_path, beam_size=5)
191
195
  text = " ".join([seg.text for seg in segments])
192
- cleanup_temp_files([audio_path])
196
+ try:
197
+ os.remove(audio_path)
198
+ except:
199
+ pass
193
200
  return text.strip()
194
201
  except Exception as e:
195
202
  print(colored(f"Transcription error: {e}", "red"))
npcsh/npcsh.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  import sys
3
+ import shutil
3
4
  import argparse
4
5
  import importlib.metadata
5
6
  import warnings
@@ -123,7 +124,7 @@ Welcome to {BLUE}npc{RESET}{RUST}sh{RESET}!
123
124
  {BLUE}|🤖|{RESET}
124
125
  {RUST}___________________________________________{RESET}
125
126
 
126
- Begin by asking a question, issuing a bash command, or typing '/help' for more information.
127
+ Ask a question, run a command, or type '/help' to get started.
127
128
  """)
128
129
 
129
130
 
@@ -138,35 +139,88 @@ def run_repl(command_history: CommandHistory, initial_state: ShellState, router,
138
139
  if launched_jinx:
139
140
  state, output = execute_command(f"/{launched_jinx}", state, router=router, command_history=command_history)
140
141
  process_result(f"/{launched_jinx}", state, output, command_history)
141
- else:
142
- render_markdown(f'- Using {state.current_mode} mode. Use /agent, /cmd, or /chat to switch to other modes')
143
- render_markdown('- To switch to a different NPC, type /npc <npc_name> or /n <npc_name> to switch to that NPC.')
144
- render_markdown('\n- Here are the current NPCs available in your team: ' + ', '.join([npc_name for npc_name in state.team.npcs.keys()]))
145
- # Show jinxs organized by folder using _source_path from jinx objects
146
- jinxs_by_folder = {}
142
+
143
+ # Print startup info panel
144
+ BLUE = "\033[1;94m"
145
+ RUST = "\033[1;38;5;202m"
146
+ DIM = "\033[2m"
147
+ BOLD = "\033[1m"
148
+ RESET = "\033[0m"
149
+
150
+ term_width = min(shutil.get_terminal_size().columns, 80)
151
+
152
+ def _wrap_names(names, indent=4, sep=" "):
153
+ """Wrap a list of prefixed names to fit terminal width."""
154
+ lines = []
155
+ line = " " * indent
156
+ for name in names:
157
+ if len(line) + len(name) + len(sep) > term_width and line.strip():
158
+ lines.append(line.rstrip())
159
+ line = " " * indent
160
+ line += name + sep
161
+ if line.strip():
162
+ lines.append(line.rstrip())
163
+ return "\n".join(lines)
164
+
165
+ if not launched_jinx:
166
+ print(f" {DIM}mode:{RESET} {BOLD}{state.current_mode}{RESET} {DIM}│{RESET} {DIM}switch:{RESET} /agent /cmd /chat")
167
+
168
+ # NPCs with @ prefix
169
+ npc_names = [f"@{n}" for n in state.team.npcs.keys()]
170
+ print(f" {DIM}npcs:{RESET} {BLUE}{(' ').join(npc_names)}{RESET}")
171
+
172
+ # Jinxs - organized by group, hiding incognide, computer_use, and browser
173
+ hidden_folders = {'incognide', 'computer_use', 'browser'}
174
+ jinxs_by_group = {}
147
175
  if hasattr(state.team, 'jinxs_dict'):
148
176
  for jinx_name, jinx_obj in state.team.jinxs_dict.items():
149
- folder = 'other'
177
+ group = 'other'
178
+ subgroup = None
150
179
  if hasattr(jinx_obj, '_source_path') and jinx_obj._source_path:
151
180
  parts = jinx_obj._source_path.split(os.sep)
152
181
  if 'jinxs' in parts:
153
182
  idx = parts.index('jinxs')
154
- if idx + 1 < len(parts) - 1:
155
- folder = parts[idx + 1]
183
+ remaining = parts[idx + 1:] # e.g. ['lib', 'core', 'sh.jinx']
184
+ if any(seg in hidden_folders for seg in remaining):
185
+ continue
186
+ if len(remaining) > 1:
187
+ group = remaining[0]
188
+ # For lib, use the subfolder as subgroup
189
+ if group == 'lib' and len(remaining) > 2:
190
+ subgroup = remaining[1]
156
191
  else:
157
- folder = 'root'
158
- if folder not in jinxs_by_folder:
159
- jinxs_by_folder[folder] = []
160
- jinxs_by_folder[folder].append(jinx_name)
161
-
162
- if jinxs_by_folder:
163
- folder_order = ['bin', 'lib', 'npc_studio', 'root', 'other']
164
- sorted_folders = sorted(jinxs_by_folder.keys(), key=lambda x: (folder_order.index(x) if x in folder_order else 99, x))
165
- jinx_summary = []
166
- for folder in sorted_folders:
167
- count = len(jinxs_by_folder[folder])
168
- jinx_summary.append(f"{folder}/ ({count})")
169
- render_markdown('\n- Available Jinxs: ' + ', '.join(jinx_summary) + ' — use `/jinxs` for details')
192
+ group = 'root'
193
+ key = (group, subgroup)
194
+ if key not in jinxs_by_group:
195
+ jinxs_by_group[key] = []
196
+ jinxs_by_group[key].append(jinx_name)
197
+
198
+ if jinxs_by_group:
199
+ print()
200
+ # Collect top-level groups and their subgroups
201
+ group_order = ['bin', 'modes', 'lib', 'npc_studio', 'root', 'other']
202
+ # Sort keys: by group order, then subgroup alphabetically
203
+ sorted_keys = sorted(
204
+ jinxs_by_group.keys(),
205
+ key=lambda k: (group_order.index(k[0]) if k[0] in group_order else 99, k[1] or '')
206
+ )
207
+ last_group = None
208
+ for group, subgroup in sorted_keys:
209
+ names = sorted(jinxs_by_group[(group, subgroup)])
210
+ prefixed = [f"/{n}" for n in names]
211
+ if group != last_group:
212
+ label = group
213
+ if group == 'lib' and subgroup:
214
+ label = f"lib/{subgroup}"
215
+ print(f" {RUST}{label}/{RESET}")
216
+ elif subgroup:
217
+ print(f" {RUST}lib/{subgroup}/{RESET}")
218
+ else:
219
+ print(f" {RUST}{group}/{RESET}")
220
+ print(_wrap_names(prefixed))
221
+ last_group = group
222
+ print(f"\n {DIM}/jinxs for full list{RESET}")
223
+ print()
170
224
 
171
225
  is_windows = platform.system().lower().startswith("win")
172
226
  try:
@@ -187,32 +241,40 @@ def run_repl(command_history: CommandHistory, initial_state: ShellState, router,
187
241
  try:
188
242
  print(f" -> Archiving knowledge for: T='{team_name}', N='{npc_name}', P='{path}'")
189
243
 
190
- convo_id = current_state.conversation_id
191
- all_messages = command_history.get_conversations_by_id(convo_id)
244
+ # Only build KG from approved memories, not raw conversation
245
+ memory_examples = command_history.get_memory_examples_for_context(
246
+ npc=npc_name, team=team_name, directory_path=path
247
+ )
248
+ approved = memory_examples.get("approved", []) + memory_examples.get("edited", [])
192
249
 
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
- ]
250
+ if not approved:
251
+ print(" ...No approved memories for this scope, skipping.")
252
+ continue
197
253
 
198
- full_text = "\n".join([f"{m['role']}: {m['content']}" for m in scope_messages if m.get('content')])
254
+ memory_statements = []
255
+ for mem in approved:
256
+ statement = mem.get("final_memory") or mem.get("initial_memory")
257
+ if statement:
258
+ memory_statements.append(statement)
199
259
 
200
- if not full_text.strip():
201
- print(" ...No content for this scope, skipping.")
260
+ if not memory_statements:
202
261
  continue
203
262
 
263
+ memory_text = "\n".join(f"- {s}" for s in memory_statements)
264
+ print(colored(f" evolving KG from {len(memory_statements)} approved memories...", "cyan"))
265
+
204
266
  current_kg = load_kg_from_db(engine, team_name, npc_name, path)
205
267
 
206
268
  evolved_kg, _ = kg_evolve_incremental(
207
269
  existing_kg=current_kg,
208
- new_content_text=full_text,
270
+ new_content_text=memory_text,
209
271
  model=current_state.npc.model,
210
272
  provider=current_state.npc.provider,
211
- npc= current_state.npc,
273
+ npc=current_state.npc,
212
274
  get_concepts=True,
213
- link_concepts_facts = True,
214
- link_concepts_concepts = True,
215
- link_facts_facts = True,
275
+ link_concepts_facts=True,
276
+ link_concepts_concepts=True,
277
+ link_facts_facts=True,
216
278
  )
217
279
 
218
280
  save_kg_to_db(engine,
@@ -237,16 +299,19 @@ def run_repl(command_history: CommandHistory, initial_state: ShellState, router,
237
299
  # Display usage before compacting
238
300
  display_usage(state)
239
301
 
240
- planning_state = {
241
- "goal": "ongoing npcsh session",
242
- "facts": [f"Working in {state.current_path}", f"Current mode: {state.current_mode}"],
243
- "successes": [],
244
- "mistakes": [],
245
- "todos": [],
246
- "constraints": ["Follow user requests", "Use appropriate mode for tasks"]
247
- }
248
- compressed_state = state.npc.compress_planning_state(planning_state)
249
- state.messages = [{"role": "system", "content": f"Session context: {compressed_state}"}]
302
+ # Pass actual messages so compress_planning_state summarizes real conversation
303
+ try:
304
+ compressed_state = state.npc.compress_planning_state(state.messages)
305
+ except Exception:
306
+ # Fallback: just keep recent messages if summarization fails
307
+ compressed_state = None
308
+
309
+ # Keep last 6 messages (3 exchanges) for immediate context
310
+ recent = state.messages[-6:]
311
+ if compressed_state:
312
+ state.messages = [{"role": "system", "content": f"Session context (earlier conversation summary): {compressed_state}"}] + recent
313
+ else:
314
+ state.messages = recent
250
315
 
251
316
  try:
252
317
  completer = make_completer(state, router)
npcsh/routes.py CHANGED
@@ -62,7 +62,10 @@ class CommandRouter:
62
62
  try:
63
63
  import shlex
64
64
 
65
- parts = shlex.split(command)
65
+ try:
66
+ parts = shlex.split(command)
67
+ except ValueError:
68
+ parts = command.split()
66
69
  args = parts[1:] if len(parts) > 1 else []
67
70
 
68
71
  # Use extract_jinx_inputs
File without changes