npcsh 1.0.26__py3-none-any.whl → 1.0.28__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 (50) hide show
  1. npcsh/_state.py +115 -111
  2. npcsh/alicanto.py +88 -88
  3. npcsh/corca.py +423 -95
  4. npcsh/guac.py +110 -107
  5. npcsh/mcp_helpers.py +45 -45
  6. npcsh/mcp_server.py +16 -17
  7. npcsh/npc.py +16 -17
  8. npcsh/npc_team/jinxs/bash_executer.jinx +1 -1
  9. npcsh/npc_team/jinxs/edit_file.jinx +6 -6
  10. npcsh/npc_team/jinxs/image_generation.jinx +5 -5
  11. npcsh/npc_team/jinxs/screen_cap.jinx +2 -2
  12. npcsh/npcsh.py +15 -6
  13. npcsh/plonk.py +8 -8
  14. npcsh/routes.py +77 -77
  15. npcsh/spool.py +13 -13
  16. npcsh/wander.py +37 -37
  17. npcsh/yap.py +72 -72
  18. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/bash_executer.jinx +1 -1
  19. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/edit_file.jinx +6 -6
  20. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/image_generation.jinx +5 -5
  21. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/screen_cap.jinx +2 -2
  22. {npcsh-1.0.26.dist-info → npcsh-1.0.28.dist-info}/METADATA +1 -1
  23. npcsh-1.0.28.dist-info/RECORD +73 -0
  24. npcsh-1.0.26.dist-info/RECORD +0 -73
  25. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/alicanto.npc +0 -0
  26. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/alicanto.png +0 -0
  27. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/corca.npc +0 -0
  28. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/corca.png +0 -0
  29. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/foreman.npc +0 -0
  30. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/frederic.npc +0 -0
  31. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/frederic4.png +0 -0
  32. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/guac.png +0 -0
  33. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/internet_search.jinx +0 -0
  34. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
  35. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  36. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  37. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  38. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/plonk.npc +0 -0
  39. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/plonk.png +0 -0
  40. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/plonkjr.npc +0 -0
  41. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  42. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/python_executor.jinx +0 -0
  43. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/sibiji.npc +0 -0
  44. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/sibiji.png +0 -0
  45. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/spool.png +0 -0
  46. {npcsh-1.0.26.data → npcsh-1.0.28.data}/data/npcsh/npc_team/yap.png +0 -0
  47. {npcsh-1.0.26.dist-info → npcsh-1.0.28.dist-info}/WHEEL +0 -0
  48. {npcsh-1.0.26.dist-info → npcsh-1.0.28.dist-info}/entry_points.txt +0 -0
  49. {npcsh-1.0.26.dist-info → npcsh-1.0.28.dist-info}/licenses/LICENSE +0 -0
  50. {npcsh-1.0.26.dist-info → npcsh-1.0.28.dist-info}/top_level.txt +0 -0
npcsh/plonk.py CHANGED
@@ -8,7 +8,7 @@ import matplotlib.pyplot as plt
8
8
  import matplotlib.patches as patches
9
9
  from PIL import Image
10
10
  import numpy as np
11
- import imagehash # Using perceptual hashing for smarter screen comparison
11
+ import imagehash
12
12
  from npcsh._state import NPCSH_VISION_MODEL, NPCSH_VISION_PROVIDER
13
13
  import argparse
14
14
  from npcpy.npc_compiler import NPC
@@ -40,7 +40,7 @@ def format_plonk_summary(synthesized_summary: list) -> str:
40
40
  def get_image_hash(image_path):
41
41
  """Generate a perceptual hash of the image to detect screen changes intelligently."""
42
42
  try:
43
- # Perceptual hash is more robust to minor changes like a blinking cursor
43
+
44
44
  return imagehash.phash(Image.open(image_path))
45
45
  except Exception as e:
46
46
  print(f"Could not generate image hash: {e}")
@@ -59,7 +59,7 @@ def add_click_vector_trail(image_path, click_history, output_path):
59
59
  font_size = max(12, min(width, height) // 80)
60
60
  colors = plt.cm.viridis(np.linspace(0.3, 1.0, len(click_history)))
61
61
 
62
- # Draw arrows connecting clicks first
62
+
63
63
  if len(click_history) > 1:
64
64
  for i in range(len(click_history) - 1):
65
65
  x1, y1 = (click_history[i]['x'] * width / 100, click_history[i]['y'] * height / 100)
@@ -68,7 +68,7 @@ def add_click_vector_trail(image_path, click_history, output_path):
68
68
  arrowprops=dict(arrowstyle='->,head_width=0.6,head_length=0.8',
69
69
  lw=3, color='cyan', alpha=0.9, shrinkA=25, shrinkB=25))
70
70
 
71
- # Draw numbered points and coordinate labels for ALL points
71
+
72
72
  for i, click in enumerate(click_history):
73
73
  x_pixel = int(click['x'] * width / 100)
74
74
  y_pixel = int(click['y'] * height / 100)
@@ -79,15 +79,15 @@ def add_click_vector_trail(image_path, click_history, output_path):
79
79
  facecolor=colors[i], alpha=0.9)
80
80
  ax.add_patch(circle)
81
81
 
82
- # Draw the number inside the circle
82
+
83
83
  ax.text(x_pixel, y_pixel, str(i+1),
84
84
  fontsize=font_size + 4,
85
85
  color='white', weight='bold', ha='center', va='center')
86
86
 
87
- # FIXED: Draw the coordinate text label for EVERY point
87
+
88
88
  coord_text = f"({click['x']}, {click['y']})"
89
- ax.text(x_pixel + radius + 5, # Position text to the right of the circle
90
- y_pixel, # Vertically centered with the circle
89
+ ax.text(x_pixel + radius + 5,
90
+ y_pixel,
91
91
  coord_text,
92
92
  fontsize=font_size,
93
93
  color='white',
npcsh/routes.py CHANGED
@@ -1,4 +1,4 @@
1
- # --- START OF FILE routes.py ---
1
+
2
2
 
3
3
  from typing import Callable, Dict, Any, List, Optional, Union
4
4
  import functools
@@ -192,15 +192,15 @@ def safe_get(kwargs, key, default=None):
192
192
 
193
193
  @router.route("breathe", "Condense context on a regular cadence")
194
194
  def breathe_handler(command: str, **kwargs):
195
- #try:
195
+
196
196
  result = breathe(**kwargs)
197
197
  if isinstance(result, dict):
198
198
  return result
199
- #except NameError:
200
- # return {"output": "Breathe function not available."}
201
- #except Exception as e:
202
- # traceback.print_exc()
203
- # return {"output": f"Error during breathe: {e}"}
199
+
200
+
201
+
202
+
203
+
204
204
 
205
205
  @router.route("compile", "Compile NPC profiles")
206
206
  def compile_handler(command: str, **kwargs):
@@ -308,7 +308,7 @@ def help_handler(command: str, **kwargs):
308
308
  parts = shlex.split(command)
309
309
  if len(parts) < 2:
310
310
  return {"output": get_help_text(), "messages": messages}
311
- target = parts[1].lstrip('/') # User might type /help /sample, so we clean it.
311
+ target = parts[1].lstrip('/')
312
312
  output = ""
313
313
 
314
314
 
@@ -330,7 +330,7 @@ def help_handler(command: str, **kwargs):
330
330
  output += f"- **Associated Jinxs**: {jinx_names}\n"
331
331
  return {"output": output, "messages": messages}
332
332
 
333
- # 3. Is it a Jinx?
333
+
334
334
  npc = safe_get(kwargs, 'npc')
335
335
  jinx_obj = None
336
336
  source = ""
@@ -363,10 +363,10 @@ def init_handler(command: str, **kwargs):
363
363
  directory = "."
364
364
  templates = None
365
365
  context = None
366
- # Basic parsing example (needs improvement for robust flag handling)
366
+
367
367
  if len(parts) > 1 and not parts[1].startswith("-"):
368
368
  directory = parts[1]
369
- # Add logic here to parse -t, -ctx flags if needed
369
+
370
370
 
371
371
  initialize_npc_project(
372
372
  directory=directory,
@@ -400,10 +400,10 @@ def ensure_repo():
400
400
 
401
401
  def install_dependencies():
402
402
  """Install npm and pip dependencies."""
403
- # Install frontend deps
403
+
404
404
  subprocess.check_call(["npm", "install"], cwd=NPC_STUDIO_DIR)
405
405
 
406
- # Install backend deps
406
+
407
407
  req_file = NPC_STUDIO_DIR / "requirements.txt"
408
408
  if req_file.exists():
409
409
  subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", str(req_file)])
@@ -415,21 +415,21 @@ def launch_npc_studio(path_to_open: str = None):
415
415
  ensure_repo()
416
416
  install_dependencies()
417
417
 
418
- # Start backend (Flask server)
418
+
419
419
  backend = subprocess.Popen(
420
420
  [sys.executable, "npc_studio_serve.py"],
421
421
  cwd=NPC_STUDIO_DIR,
422
422
  shell = False
423
423
  )
424
424
 
425
- # Start server (Electron)
425
+
426
426
  dev_server = subprocess.Popen(
427
427
  ["npm", "run", "dev"],
428
428
  cwd=NPC_STUDIO_DIR,
429
429
  shell=False
430
430
  )
431
431
 
432
- # Start frontend (Electron)
432
+
433
433
  frontend = subprocess.Popen(
434
434
  ["npm", "start"],
435
435
  cwd=NPC_STUDIO_DIR,
@@ -437,7 +437,7 @@ def launch_npc_studio(path_to_open: str = None):
437
437
  )
438
438
 
439
439
  return backend, dev_server, frontend
440
- # ========== Router handler ==========
440
+
441
441
  @router.route("npc-studio", "Start npc studio")
442
442
  def npc_studio_handler(command: str, **kwargs):
443
443
  messages = kwargs.get("messages", [])
@@ -529,13 +529,13 @@ def plan_handler(command: str, **kwargs):
529
529
  user_command = " ".join(command.split()[1:])
530
530
  if not user_command:
531
531
  return {"output": "Usage: /plan <description_of_plan>", "messages": messages}
532
- #try:
532
+
533
533
  return execute_plan_command(command=user_command, **kwargs)
534
534
 
535
- #return {"output": "Plan function (execute_plan_command) not available.", "messages": messages}
536
- #except Exception as e:
537
- # traceback.print_exc()
538
- # return {"output": f"Error executing plan: {e}", "messages": messages}
535
+
536
+
537
+
538
+
539
539
 
540
540
  @router.route("pti", "Enter Pardon-The-Interruption mode for human-in-the-loop reasoning.")
541
541
  def pti_handler(command: str, **kwargs):
@@ -545,8 +545,8 @@ def pti_handler(command: str, **kwargs):
545
545
  def plonk_handler(command: str, **kwargs):
546
546
  messages = safe_get(kwargs, "messages", [])
547
547
 
548
- # FIXED: Use the pre-parsed positional arguments for the request,
549
- # leaving flags to be handled by kwargs.
548
+
549
+
550
550
  positional_args = safe_get(kwargs, 'positional_args', [])
551
551
  request_str = " ".join(positional_args)
552
552
 
@@ -556,14 +556,14 @@ def plonk_handler(command: str, **kwargs):
556
556
  try:
557
557
  plonk_context = safe_get(kwargs, 'plonk_context')
558
558
 
559
- # This part now works automatically with CLI flags because they are in kwargs
559
+
560
560
  summary_data = execute_plonk_command(
561
561
  request=request_str,
562
562
  model=safe_get(kwargs, 'vmodel', NPCSH_VISION_MODEL),
563
563
  provider=safe_get(kwargs, 'vprovider', NPCSH_VISION_PROVIDER),
564
564
  npc=safe_get(kwargs, 'npc'),
565
565
  plonk_context=plonk_context,
566
- debug=True # Or could be controlled by a flag: safe_get(kwargs, 'debug', False)
566
+ debug=True
567
567
  )
568
568
 
569
569
  if summary_data and isinstance(summary_data, list):
@@ -586,11 +586,11 @@ def brainblast_handler(command: str, **kwargs):
586
586
  if not search_query:
587
587
  return {"output": "Usage: /brainblast <search_terms>", "messages": messages}
588
588
 
589
- # Get the command history instance
589
+
590
590
  command_history = kwargs.get('command_history')
591
591
  if not command_history:
592
- #print('no command history provided to brainblast')
593
- # Create a new one if not provided
592
+
593
+
594
594
  db_path = safe_get(kwargs, "history_db_path", os.path.expanduser('~/npcsh_history.db'))
595
595
  try:
596
596
  command_history = CommandHistory(db_path)
@@ -599,11 +599,11 @@ def brainblast_handler(command: str, **kwargs):
599
599
  return {"output": f"Error connecting to command history: {e}", "messages": messages}
600
600
 
601
601
  try:
602
- # Remove messages from kwargs to avoid duplicate argument error
602
+
603
603
  if 'messages' in kwargs:
604
604
  del kwargs['messages']
605
605
 
606
- # Execute the brainblast command
606
+
607
607
  return execute_brainblast_command(
608
608
  command=search_query,
609
609
  **kwargs)
@@ -617,17 +617,17 @@ def rag_handler(command: str, **kwargs):
617
617
  user_command = []
618
618
  file_paths = []
619
619
 
620
- i = 1 # Skip the first element which is "rag"
620
+ i = 1
621
621
  while i < len(parts):
622
622
  if parts[i] == "-f" or parts[i] == "--file":
623
- # We found a file flag, get the file path
623
+
624
624
  if i + 1 < len(parts):
625
625
  file_paths.append(parts[i + 1])
626
- i += 2 # Skip both the flag and the path
626
+ i += 2
627
627
  else:
628
628
  return {"output": "Error: -f/--file flag needs a file path", "messages": messages}
629
629
  else:
630
- # This is part of the user query
630
+
631
631
  user_command.append(parts[i])
632
632
  i += 1
633
633
 
@@ -641,7 +641,7 @@ def rag_handler(command: str, **kwargs):
641
641
  return {"output": "Usage: /rag [-f file_path] <query>", "messages": kwargs.get('messages', [])}
642
642
 
643
643
  try:
644
- # Process files if provided
644
+
645
645
  file_contents = []
646
646
  for file_path in file_paths:
647
647
  try:
@@ -718,7 +718,7 @@ def sample_handler(command: str, **kwargs):
718
718
  "npc":kwargs.get("npc"),
719
719
  }
720
720
  else:
721
- # Handle cases where get_llm_response might fail and return something unexpected
721
+
722
722
  return {"output": str(result), "messages": messages}
723
723
 
724
724
  except Exception as e:
@@ -728,16 +728,16 @@ def sample_handler(command: str, **kwargs):
728
728
  def search_handler(command: str, **kwargs):
729
729
  """
730
730
  Executes a search command.
731
- # search commands will bel ike :
732
- # '/search "search term" '
733
- # '/search -sp perplexity ..
734
- # '/search -sp google ..
735
- # extract provider if its there
736
- # check for either -p or --p
731
+
732
+
733
+
734
+
735
+
736
+
737
737
  """
738
738
  messages = safe_get(kwargs, "messages", [])
739
739
 
740
- # The query is now in 'positional_args'
740
+
741
741
  positional_args = safe_get(kwargs, 'positional_args', [])
742
742
  query = " ".join(positional_args)
743
743
 
@@ -764,11 +764,11 @@ def search_handler(command: str, **kwargs):
764
764
 
765
765
  @router.route("serve", "Serve an NPC Team")
766
766
  def serve_handler(command: str, **kwargs):
767
- #print('calling serve handler')
768
- #print(kwargs)
767
+
768
+
769
769
 
770
770
  port = safe_get(kwargs, "port", 5337)
771
- #print(port, type(port))
771
+
772
772
  messages = safe_get(kwargs, "messages", [])
773
773
  cors = safe_get(kwargs, "cors", None)
774
774
  if cors:
@@ -821,13 +821,13 @@ def sleep_handler(command: str, **kwargs):
821
821
  if operations_str and isinstance(operations_str, str):
822
822
  operations_config = [op.strip() for op in operations_str.split(',')]
823
823
 
824
- # Define the scope variables clearly at the start
824
+
825
825
  team_name = team.name if team else "__none__"
826
826
  npc_name = npc.name if isinstance(npc, NPC) else "__none__"
827
827
  current_path = os.getcwd()
828
828
  scope_str = f"Team: '{team_name}', NPC: '{npc_name}', Path: '{current_path}'"
829
829
 
830
- # ADDED: Log the scope being checked for clarity
830
+
831
831
  render_markdown(f"- Checking knowledge graph for scope: {scope_str}")
832
832
 
833
833
  try:
@@ -840,20 +840,20 @@ def sleep_handler(command: str, **kwargs):
840
840
  try:
841
841
  current_kg = load_kg_from_db(engine, team_name, npc_name, current_path)
842
842
 
843
- # FIXED: Provide a detailed and helpful message when the KG is empty
843
+
844
844
  if not current_kg or not current_kg.get('facts'):
845
845
  output_msg = f"Knowledge graph for the current scope is empty. Nothing to process.\n"
846
846
  output_msg += f" - Scope Checked: {scope_str}\n\n"
847
847
  output_msg += "**Hint:** Have a conversation or run some commands first to build up knowledge in this specific context. The KG is unique to each combination of Team, NPC, and directory."
848
848
  return {"output": output_msg, "messages": messages}
849
849
 
850
- # Store initial stats for the final report
850
+
851
851
  original_facts = len(current_kg.get('facts', []))
852
852
  original_concepts = len(current_kg.get('concepts', []))
853
853
 
854
- # --- SEQUENTIAL EXECUTION ---
854
+
855
855
 
856
- # 1. Always run the sleep process for maintenance first.
856
+
857
857
  process_type = "Sleep"
858
858
  ops_display = f"with operations: {operations_config}" if operations_config else "with random operations"
859
859
  render_markdown(f"- Initiating sleep process {ops_display}")
@@ -866,7 +866,7 @@ def sleep_handler(command: str, **kwargs):
866
866
  operations_config=operations_config
867
867
  )
868
868
 
869
- # 2. If --dream is specified, run the dream process on the *result* of the sleep process.
869
+
870
870
  if is_dreaming:
871
871
  process_type += " & Dream"
872
872
  render_markdown(f"- Initiating dream process on the evolved KG...")
@@ -877,10 +877,10 @@ def sleep_handler(command: str, **kwargs):
877
877
  npc=npc
878
878
  )
879
879
 
880
- # 3. Save the final state of the KG back to the database
880
+
881
881
  save_kg_to_db(conn, evolved_kg, team_name, npc_name, current_path)
882
882
 
883
- # 4. Report the final, cumulative changes back to the user
883
+
884
884
  new_facts = len(evolved_kg.get('facts', []))
885
885
  new_concepts = len(evolved_kg.get('concepts', []))
886
886
 
@@ -978,7 +978,7 @@ def vixynt_handler(command: str, **kwargs):
978
978
  width = safe_get(kwargs, 'width', 1024)
979
979
  output_file_base = safe_get(kwargs, 'output_file')
980
980
  attachments = safe_get(kwargs, 'attachments')
981
- n_images = safe_get(kwargs, 'n_images', 1) # Get n_images from kwargs
981
+ n_images = safe_get(kwargs, 'n_images', 1)
982
982
  if isinstance(attachments, str):
983
983
  attachments = attachments.split(',')
984
984
 
@@ -990,7 +990,7 @@ def vixynt_handler(command: str, **kwargs):
990
990
  return {"output": "Usage: /vixynt <prompt> [--output_file path] [--attachments path] [--n_images num]", "messages": messages}
991
991
 
992
992
  try:
993
- # Call gen_image, passing n_images and expecting a list of images
993
+
994
994
  images_list = gen_image(
995
995
  prompt=user_prompt,
996
996
  model=model,
@@ -998,7 +998,7 @@ def vixynt_handler(command: str, **kwargs):
998
998
  npc=npc,
999
999
  height=height,
1000
1000
  width=width,
1001
- n_images=n_images, # Pass n_images
1001
+ n_images=n_images,
1002
1002
  input_images=attachments
1003
1003
  )
1004
1004
 
@@ -1048,33 +1048,33 @@ def vixynt_handler(command: str, **kwargs):
1048
1048
  def wander_handler(command: str, **kwargs):
1049
1049
  messages = safe_get(kwargs, "messages", [])
1050
1050
 
1051
- # General parser for key=value arguments
1051
+
1052
1052
  try:
1053
1053
  parts = shlex.split(command)
1054
1054
  problem_parts = []
1055
1055
  wander_params = {}
1056
1056
 
1057
- i = 1 # Start after the 'wander' command name
1057
+ i = 1
1058
1058
  while i < len(parts):
1059
1059
  part = parts[i]
1060
1060
 
1061
1061
  if '=' in part:
1062
- # This is the start of a key=value pair
1062
+
1063
1063
  key, initial_value = part.split('=', 1)
1064
1064
 
1065
- # Consume all subsequent parts that do NOT contain '=' as part of this value
1065
+
1066
1066
  value_parts = [initial_value]
1067
1067
  j = i + 1
1068
1068
  while j < len(parts) and '=' not in parts[j]:
1069
1069
  value_parts.append(parts[j])
1070
1070
  j += 1
1071
1071
 
1072
- # Join the reconstructed value and store it
1072
+
1073
1073
  wander_params[key] = " ".join(value_parts)
1074
- # Advance the main loop index past the consumed parts
1074
+
1075
1075
  i = j
1076
1076
  else:
1077
- # This part belongs to the problem string
1077
+
1078
1078
  problem_parts.append(part)
1079
1079
  i += 1
1080
1080
 
@@ -1086,13 +1086,13 @@ def wander_handler(command: str, **kwargs):
1086
1086
  return {"output": "Usage: /wander <problem> [key=value...]", "messages": messages}
1087
1087
 
1088
1088
  try:
1089
- # Build the argument list for enter_wander_mode
1089
+
1090
1090
  mode_args = {
1091
1091
  'problem': problem,
1092
1092
  'npc': safe_get(kwargs, 'npc'),
1093
1093
  'model': safe_get(kwargs, 'model'),
1094
1094
  'provider': safe_get(kwargs, 'provider'),
1095
- # Use parsed params with defaults
1095
+
1096
1096
  'environment': wander_params.get('environment'),
1097
1097
  'low_temp': float(wander_params.get('low-temp', 0.5)),
1098
1098
  'high_temp': float(wander_params.get('high-temp', 1.9)),
@@ -1131,10 +1131,10 @@ def yap_handler(command: str, **kwargs):
1131
1131
  def alicanto_handler(command: str, **kwargs):
1132
1132
  messages = safe_get(kwargs, "messages", [])
1133
1133
 
1134
- # Parse command with shlex to properly handle quoted strings
1134
+
1135
1135
  parts = shlex.split(command)
1136
1136
 
1137
- # Process arguments
1137
+
1138
1138
  query = ""
1139
1139
  num_npcs = safe_get(kwargs, 'num_npcs', 5)
1140
1140
  depth = safe_get(kwargs, 'depth', 3)
@@ -1142,11 +1142,11 @@ def alicanto_handler(command: str, **kwargs):
1142
1142
  creativity_factor = safe_get(kwargs, 'creativity', 0.5)
1143
1143
  output_format = safe_get(kwargs, 'format', 'report')
1144
1144
 
1145
- # Parse command-line arguments
1146
- i = 1 # Skip "alicanto" command
1145
+
1146
+ i = 1
1147
1147
  while i < len(parts):
1148
1148
  if parts[i].startswith('--'):
1149
- option = parts[i][2:] # Remove '--'
1149
+ option = parts[i][2:]
1150
1150
  if option in ['num-npcs', 'npcs']:
1151
1151
  if i + 1 < len(parts) and parts[i + 1].isdigit():
1152
1152
  num_npcs = int(parts[i + 1])
@@ -1178,16 +1178,16 @@ def alicanto_handler(command: str, **kwargs):
1178
1178
  else:
1179
1179
  i += 1
1180
1180
  else:
1181
- # Skip unknown option
1181
+
1182
1182
  i += 1
1183
1183
  else:
1184
- # This is part of the request
1184
+
1185
1185
  query += parts[i] + " "
1186
1186
  i += 1
1187
1187
 
1188
1188
  query = query.strip()
1189
1189
 
1190
- # Also apply any kwargs that were passed directly (these override command line args)
1190
+
1191
1191
  if 'num_npcs' in kwargs:
1192
1192
  try:
1193
1193
  num_npcs = int(kwargs['num_npcs'])
@@ -1230,7 +1230,7 @@ def alicanto_handler(command: str, **kwargs):
1230
1230
  output_format=output_format
1231
1231
  )
1232
1232
 
1233
- # Format the output based on the result type
1233
+
1234
1234
  if isinstance(result, dict):
1235
1235
  if "integration" in result:
1236
1236
  output = result["integration"]
npcsh/spool.py CHANGED
@@ -51,10 +51,10 @@ def enter_spool_mode(
51
51
  **kwargs,
52
52
  ) -> Dict:
53
53
  print_spool_ascii()
54
- # Initialize state using existing infrastructure
54
+
55
55
  command_history, state_team, default_npc = setup_shell()
56
56
 
57
- # Create spool state, inheriting from initial_state
57
+
58
58
  spool_state = ShellState(
59
59
  npc=npc or default_npc,
60
60
  team=team or state_team,
@@ -65,7 +65,7 @@ def enter_spool_mode(
65
65
  attachments=None,
66
66
  )
67
67
 
68
- # Override models/providers if specified
68
+
69
69
  if model:
70
70
  spool_state.chat_model = model
71
71
  if provider:
@@ -79,7 +79,7 @@ def enter_spool_mode(
79
79
  print(f"🧵 Entering spool mode{npc_info}. Type '/sq' to exit spool mode.")
80
80
  print("💡 Tip: Press Ctrl+C during streaming to interrupt and continue with a new message.")
81
81
 
82
- # Handle file loading
82
+
83
83
  loaded_chunks = {}
84
84
  if attachments:
85
85
  if isinstance(attachments, str):
@@ -97,14 +97,14 @@ def enter_spool_mode(
97
97
  except Exception as e:
98
98
  print(colored(f"Error loading {file_path}: {str(e)}", "red"))
99
99
 
100
- # Initialize context with system message if needed
100
+
101
101
  if not spool_state.messages or spool_state.messages[0].get("role") != "system":
102
102
  system_message = get_system_message(spool_state.npc) if spool_state.npc else "You are a helpful assistant."
103
103
  spool_state.messages.insert(0, {"role": "system", "content": system_message})
104
104
 
105
105
  while True:
106
106
  try:
107
- # Use consistent prompt styling with npcsh
107
+
108
108
  npc_name = spool_state.npc.name if spool_state.npc else "chat"
109
109
  display_model = spool_state.npc.model if spool_state.npc and spool_state.npc.model else spool_state.chat_model
110
110
 
@@ -123,7 +123,7 @@ def enter_spool_mode(
123
123
  spool_state.messages = enter_yap_mode(spool_state.messages, spool_state.npc)
124
124
  continue
125
125
 
126
- # Handle vision commands
126
+
127
127
  if user_input.startswith("/ots"):
128
128
  command_parts = user_input.split()
129
129
  image_paths = []
@@ -146,7 +146,7 @@ def enter_spool_mode(
146
146
 
147
147
  vision_prompt = input("Prompt for image(s) (or press Enter): ").strip() or "Describe these images."
148
148
 
149
- # Use vision models for image processing
149
+
150
150
  response = get_llm_response(
151
151
  vision_prompt,
152
152
  model=spool_state.vision_model,
@@ -162,11 +162,11 @@ def enter_spool_mode(
162
162
  spool_state.messages = response.get('messages', spool_state.messages)
163
163
  output = response.get('response')
164
164
 
165
- # Process and display the result
165
+
166
166
  process_result(vision_prompt, spool_state, {'output': output}, command_history)
167
167
  continue
168
168
 
169
- # Handle RAG context if files are loaded
169
+
170
170
  current_prompt = user_input
171
171
  if loaded_chunks:
172
172
  context_content = ""
@@ -183,7 +183,7 @@ def enter_spool_mode(
183
183
  if context_content:
184
184
  current_prompt += f"\n\n--- Relevant context from loaded files ---\n{context_content}"
185
185
 
186
- # Use standard LLM processing
186
+
187
187
  response = get_llm_response(
188
188
  current_prompt,
189
189
  model=spool_state.npc.model if spool_state.npc and spool_state.npc.model else spool_state.chat_model,
@@ -197,7 +197,7 @@ def enter_spool_mode(
197
197
  spool_state.messages = response.get('messages', spool_state.messages)
198
198
  output = response.get('response')
199
199
 
200
- # Use existing result processing
200
+
201
201
  process_result(current_prompt, spool_state, {'output': output}, command_history)
202
202
 
203
203
  except (EOFError,):
@@ -221,7 +221,7 @@ def main():
221
221
 
222
222
  args = parser.parse_args()
223
223
 
224
- # Use existing infrastructure to get NPC
224
+
225
225
  command_history, team, default_npc = setup_shell()
226
226
 
227
227
  npc = None