npcsh 1.1.14__py3-none-any.whl → 1.1.16__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 (168) hide show
  1. npcsh/_state.py +533 -80
  2. npcsh/mcp_server.py +2 -1
  3. npcsh/npc.py +84 -32
  4. npcsh/npc_team/alicanto.npc +22 -1
  5. npcsh/npc_team/corca.npc +28 -9
  6. npcsh/npc_team/frederic.npc +25 -4
  7. npcsh/npc_team/guac.npc +22 -0
  8. npcsh/npc_team/jinxs/bin/nql.jinx +141 -0
  9. npcsh/npc_team/jinxs/bin/sync.jinx +230 -0
  10. {npcsh-1.1.14.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/bin}/vixynt.jinx +8 -30
  11. npcsh/npc_team/jinxs/bin/wander.jinx +152 -0
  12. npcsh/npc_team/jinxs/lib/browser/browser_action.jinx +220 -0
  13. npcsh/npc_team/jinxs/lib/browser/browser_screenshot.jinx +40 -0
  14. npcsh/npc_team/jinxs/lib/browser/close_browser.jinx +14 -0
  15. npcsh/npc_team/jinxs/lib/browser/open_browser.jinx +43 -0
  16. npcsh/npc_team/jinxs/lib/computer_use/click.jinx +23 -0
  17. npcsh/npc_team/jinxs/lib/computer_use/key_press.jinx +26 -0
  18. npcsh/npc_team/jinxs/lib/computer_use/launch_app.jinx +37 -0
  19. npcsh/npc_team/jinxs/lib/computer_use/screenshot.jinx +23 -0
  20. npcsh/npc_team/jinxs/lib/computer_use/type_text.jinx +27 -0
  21. npcsh/npc_team/jinxs/lib/computer_use/wait.jinx +21 -0
  22. {npcsh-1.1.14.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/lib/core}/edit_file.jinx +3 -3
  23. {npcsh-1.1.14.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/lib/core}/load_file.jinx +1 -1
  24. npcsh/npc_team/jinxs/lib/core/paste.jinx +134 -0
  25. {npcsh-1.1.14.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/lib/core}/search.jinx +2 -1
  26. npcsh/npc_team/jinxs/{code → lib/core}/sh.jinx +2 -8
  27. npcsh/npc_team/jinxs/{code → lib/core}/sql.jinx +1 -1
  28. npcsh/npc_team/jinxs/lib/orchestration/convene.jinx +232 -0
  29. npcsh/npc_team/jinxs/lib/orchestration/delegate.jinx +184 -0
  30. npcsh/npc_team/jinxs/lib/research/arxiv.jinx +76 -0
  31. npcsh/npc_team/jinxs/lib/research/paper_search.jinx +101 -0
  32. npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +69 -0
  33. npcsh/npc_team/jinxs/{utils/core → lib/utils}/build.jinx +8 -8
  34. npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +176 -0
  35. npcsh/npc_team/jinxs/lib/utils/shh.jinx +17 -0
  36. npcsh/npc_team/jinxs/lib/utils/switch.jinx +62 -0
  37. npcsh/npc_team/jinxs/lib/utils/switches.jinx +61 -0
  38. npcsh/npc_team/jinxs/lib/utils/teamviz.jinx +205 -0
  39. npcsh/npc_team/jinxs/lib/utils/verbose.jinx +17 -0
  40. npcsh/npc_team/kadiefa.npc +19 -1
  41. npcsh/npc_team/plonk.npc +26 -1
  42. npcsh/npc_team/plonkjr.npc +22 -1
  43. npcsh/npc_team/sibiji.npc +23 -2
  44. npcsh/npcsh.py +153 -39
  45. npcsh/ui.py +22 -1
  46. npcsh-1.1.16.data/data/npcsh/npc_team/alicanto.npc +23 -0
  47. npcsh-1.1.16.data/data/npcsh/npc_team/arxiv.jinx +76 -0
  48. npcsh-1.1.16.data/data/npcsh/npc_team/browser_action.jinx +220 -0
  49. npcsh-1.1.16.data/data/npcsh/npc_team/browser_screenshot.jinx +40 -0
  50. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/build.jinx +8 -8
  51. npcsh-1.1.16.data/data/npcsh/npc_team/click.jinx +23 -0
  52. npcsh-1.1.16.data/data/npcsh/npc_team/close_browser.jinx +14 -0
  53. npcsh-1.1.16.data/data/npcsh/npc_team/convene.jinx +232 -0
  54. npcsh-1.1.16.data/data/npcsh/npc_team/corca.npc +31 -0
  55. npcsh-1.1.16.data/data/npcsh/npc_team/delegate.jinx +184 -0
  56. {npcsh/npc_team/jinxs/utils → npcsh-1.1.16.data/data/npcsh/npc_team}/edit_file.jinx +3 -3
  57. npcsh-1.1.16.data/data/npcsh/npc_team/frederic.npc +27 -0
  58. npcsh-1.1.16.data/data/npcsh/npc_team/guac.npc +22 -0
  59. npcsh-1.1.16.data/data/npcsh/npc_team/jinxs.jinx +176 -0
  60. npcsh-1.1.16.data/data/npcsh/npc_team/kadiefa.npc +21 -0
  61. npcsh-1.1.16.data/data/npcsh/npc_team/key_press.jinx +26 -0
  62. npcsh-1.1.16.data/data/npcsh/npc_team/launch_app.jinx +37 -0
  63. {npcsh/npc_team/jinxs/utils → npcsh-1.1.16.data/data/npcsh/npc_team}/load_file.jinx +1 -1
  64. npcsh-1.1.16.data/data/npcsh/npc_team/nql.jinx +141 -0
  65. npcsh-1.1.16.data/data/npcsh/npc_team/open_browser.jinx +43 -0
  66. npcsh-1.1.16.data/data/npcsh/npc_team/paper_search.jinx +101 -0
  67. npcsh-1.1.16.data/data/npcsh/npc_team/paste.jinx +134 -0
  68. npcsh-1.1.16.data/data/npcsh/npc_team/plonk.npc +27 -0
  69. npcsh-1.1.16.data/data/npcsh/npc_team/plonkjr.npc +23 -0
  70. npcsh-1.1.16.data/data/npcsh/npc_team/screenshot.jinx +23 -0
  71. {npcsh/npc_team/jinxs/utils → npcsh-1.1.16.data/data/npcsh/npc_team}/search.jinx +2 -1
  72. npcsh-1.1.16.data/data/npcsh/npc_team/semantic_scholar.jinx +69 -0
  73. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/sh.jinx +2 -8
  74. npcsh-1.1.16.data/data/npcsh/npc_team/shh.jinx +17 -0
  75. npcsh-1.1.16.data/data/npcsh/npc_team/sibiji.npc +24 -0
  76. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/sql.jinx +1 -1
  77. npcsh-1.1.16.data/data/npcsh/npc_team/switch.jinx +62 -0
  78. npcsh-1.1.16.data/data/npcsh/npc_team/switches.jinx +61 -0
  79. npcsh-1.1.16.data/data/npcsh/npc_team/sync.jinx +230 -0
  80. npcsh-1.1.16.data/data/npcsh/npc_team/teamviz.jinx +205 -0
  81. npcsh-1.1.16.data/data/npcsh/npc_team/type_text.jinx +27 -0
  82. npcsh-1.1.16.data/data/npcsh/npc_team/verbose.jinx +17 -0
  83. {npcsh/npc_team/jinxs/utils → npcsh-1.1.16.data/data/npcsh/npc_team}/vixynt.jinx +8 -30
  84. npcsh-1.1.16.data/data/npcsh/npc_team/wait.jinx +21 -0
  85. npcsh-1.1.16.data/data/npcsh/npc_team/wander.jinx +152 -0
  86. {npcsh-1.1.14.dist-info → npcsh-1.1.16.dist-info}/METADATA +399 -58
  87. npcsh-1.1.16.dist-info/RECORD +170 -0
  88. npcsh-1.1.16.dist-info/entry_points.txt +19 -0
  89. npcsh-1.1.16.dist-info/top_level.txt +2 -0
  90. project/__init__.py +1 -0
  91. npcsh/npc_team/foreman.npc +0 -7
  92. npcsh/npc_team/jinxs/modes/alicanto.jinx +0 -194
  93. npcsh/npc_team/jinxs/modes/corca.jinx +0 -249
  94. npcsh/npc_team/jinxs/modes/guac.jinx +0 -317
  95. npcsh/npc_team/jinxs/modes/plonk.jinx +0 -214
  96. npcsh/npc_team/jinxs/modes/pti.jinx +0 -170
  97. npcsh/npc_team/jinxs/modes/wander.jinx +0 -186
  98. npcsh/npc_team/jinxs/utils/agent.jinx +0 -17
  99. npcsh/npc_team/jinxs/utils/core/jinxs.jinx +0 -32
  100. npcsh-1.1.14.data/data/npcsh/npc_team/agent.jinx +0 -17
  101. npcsh-1.1.14.data/data/npcsh/npc_team/alicanto.jinx +0 -194
  102. npcsh-1.1.14.data/data/npcsh/npc_team/alicanto.npc +0 -2
  103. npcsh-1.1.14.data/data/npcsh/npc_team/corca.jinx +0 -249
  104. npcsh-1.1.14.data/data/npcsh/npc_team/corca.npc +0 -12
  105. npcsh-1.1.14.data/data/npcsh/npc_team/foreman.npc +0 -7
  106. npcsh-1.1.14.data/data/npcsh/npc_team/frederic.npc +0 -6
  107. npcsh-1.1.14.data/data/npcsh/npc_team/guac.jinx +0 -317
  108. npcsh-1.1.14.data/data/npcsh/npc_team/jinxs.jinx +0 -32
  109. npcsh-1.1.14.data/data/npcsh/npc_team/kadiefa.npc +0 -3
  110. npcsh-1.1.14.data/data/npcsh/npc_team/plonk.jinx +0 -214
  111. npcsh-1.1.14.data/data/npcsh/npc_team/plonk.npc +0 -2
  112. npcsh-1.1.14.data/data/npcsh/npc_team/plonkjr.npc +0 -2
  113. npcsh-1.1.14.data/data/npcsh/npc_team/pti.jinx +0 -170
  114. npcsh-1.1.14.data/data/npcsh/npc_team/sibiji.npc +0 -3
  115. npcsh-1.1.14.data/data/npcsh/npc_team/wander.jinx +0 -186
  116. npcsh-1.1.14.dist-info/RECORD +0 -135
  117. npcsh-1.1.14.dist-info/entry_points.txt +0 -9
  118. npcsh-1.1.14.dist-info/top_level.txt +0 -1
  119. /npcsh/npc_team/jinxs/{utils → bin}/roll.jinx +0 -0
  120. /npcsh/npc_team/jinxs/{utils → bin}/sample.jinx +0 -0
  121. /npcsh/npc_team/jinxs/{modes → bin}/spool.jinx +0 -0
  122. /npcsh/npc_team/jinxs/{modes → bin}/yap.jinx +0 -0
  123. /npcsh/npc_team/jinxs/{utils → lib/computer_use}/trigger.jinx +0 -0
  124. /npcsh/npc_team/jinxs/{utils → lib/core}/chat.jinx +0 -0
  125. /npcsh/npc_team/jinxs/{utils → lib/core}/cmd.jinx +0 -0
  126. /npcsh/npc_team/jinxs/{utils → lib/core}/compress.jinx +0 -0
  127. /npcsh/npc_team/jinxs/{utils → lib/core}/ots.jinx +0 -0
  128. /npcsh/npc_team/jinxs/{code → lib/core}/python.jinx +0 -0
  129. /npcsh/npc_team/jinxs/{utils → lib/core}/sleep.jinx +0 -0
  130. /npcsh/npc_team/jinxs/{utils/core → lib/utils}/compile.jinx +0 -0
  131. /npcsh/npc_team/jinxs/{utils/core → lib/utils}/help.jinx +0 -0
  132. /npcsh/npc_team/jinxs/{utils/core → lib/utils}/init.jinx +0 -0
  133. /npcsh/npc_team/jinxs/{utils → lib/utils}/serve.jinx +0 -0
  134. /npcsh/npc_team/jinxs/{utils/core → lib/utils}/set.jinx +0 -0
  135. /npcsh/npc_team/jinxs/{utils → lib/utils}/usage.jinx +0 -0
  136. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/alicanto.png +0 -0
  137. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/chat.jinx +0 -0
  138. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/cmd.jinx +0 -0
  139. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/compile.jinx +0 -0
  140. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/compress.jinx +0 -0
  141. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/corca.png +0 -0
  142. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/corca_example.png +0 -0
  143. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/frederic4.png +0 -0
  144. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/guac.png +0 -0
  145. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/help.jinx +0 -0
  146. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/init.jinx +0 -0
  147. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  148. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/npc-studio.jinx +0 -0
  149. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  150. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  151. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/ots.jinx +0 -0
  152. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/plonk.png +0 -0
  153. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  154. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/python.jinx +0 -0
  155. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/roll.jinx +0 -0
  156. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/sample.jinx +0 -0
  157. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/serve.jinx +0 -0
  158. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/set.jinx +0 -0
  159. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/sibiji.png +0 -0
  160. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/sleep.jinx +0 -0
  161. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/spool.jinx +0 -0
  162. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/spool.png +0 -0
  163. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/trigger.jinx +0 -0
  164. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/usage.jinx +0 -0
  165. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/yap.jinx +0 -0
  166. {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/yap.png +0 -0
  167. {npcsh-1.1.14.dist-info → npcsh-1.1.16.dist-info}/WHEEL +0 -0
  168. {npcsh-1.1.14.dist-info → npcsh-1.1.16.dist-info}/licenses/LICENSE +0 -0
@@ -41,27 +41,15 @@ steps:
41
41
  except (ValueError, TypeError):
42
42
  width = 1024
43
43
 
44
- # Get model and provider, prioritizing context, then NPC, then environment variables
44
+ # Get model and provider from context or environment
45
45
  model = context.get('model')
46
46
  provider = context.get('provider')
47
-
48
- # Use NPC's model/provider as fallback
49
- if not model and npc and hasattr(npc, 'model') and npc.model:
50
- model = npc.model
51
- if not provider and npc and hasattr(npc, 'provider') and npc.provider:
52
- provider = npc.provider
53
-
47
+
54
48
  # Fallback to environment variables
55
49
  if not model:
56
50
  model = os.getenv('NPCSH_IMAGE_GEN_MODEL')
57
51
  if not provider:
58
52
  provider = os.getenv('NPCSH_IMAGE_GEN_PROVIDER')
59
-
60
- # Final hardcoded fallbacks if nothing else is set
61
- if not model:
62
- model = "runwayml/stable-diffusion-v1-5"
63
- if not provider:
64
- provider = "diffusers"
65
53
 
66
54
  # Parse attachments
67
55
  input_images = []
@@ -93,12 +81,11 @@ steps:
93
81
  images_list = result
94
82
 
95
83
  saved_files = []
96
- html_image_tags = [] # This list will store the raw HTML <img> tags
97
-
84
+
98
85
  for i, image in enumerate(images_list):
99
86
  if image is None:
100
87
  continue
101
-
88
+
102
89
  # Determine output filename
103
90
  if output_name and str(output_name).strip():
104
91
  base_name, ext = os.path.splitext(os.path.expanduser(str(output_name)))
@@ -111,25 +98,16 @@ steps:
111
98
  os.path.expanduser("~/.npcsh/images/")
112
99
  + f"image_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{i}.png"
113
100
  )
114
-
101
+
115
102
  # Save image to file
116
103
  image.save(current_output_file)
117
104
  saved_files.append(current_output_file)
118
105
 
119
- # Convert image to base64 and create an HTML <img> tag
120
- with open(current_output_file, 'rb') as f:
121
- img_data = base64.b64encode(f.read()).decode()
122
- # Using raw HTML <img> tag with data URI
123
- html_image_tags.append(f'<img src="data:image/png;base64,{img_data}" alt="Generated Image {i+1}" style="max-width: 100%; display: block; margin-top: 10px;">')
124
-
125
106
  if saved_files:
126
- output_text_message = f"Image(s) generated and saved to: {', '.join(saved_files)}"
107
+ output = f"Image(s) generated: {', '.join(saved_files)}"
127
108
  if input_images:
128
- output_text_message = f"Image(s) edited and saved to: {', '.join(saved_files)}"
129
-
130
- output = output_text_message # Keep the text message clean
131
- output += f"\n\nThe image files have been saved and are ready to view."
132
- output += "\n\n" + "\n".join(html_image_tags) # Append all HTML <img> tags to the output
109
+ output = f"Image(s) edited: {', '.join(saved_files)}"
110
+ context['generated_images'] = saved_files
133
111
  else:
134
112
  output = "No images were generated."
135
113
 
@@ -0,0 +1,152 @@
1
+ jinx_name: wander
2
+ description: Creative daydreaming with probabilistic temperature shifts mid-stream
3
+ inputs:
4
+ - problem
5
+ steps:
6
+ - name: wander_explore
7
+ engine: python
8
+ code: |
9
+ import random
10
+ from termcolor import colored
11
+ from npcpy.llm_funcs import get_llm_response
12
+
13
+ problem = context.get('problem', '')
14
+ if not problem:
15
+ context['output'] = "Need a topic to wander about."
16
+ exit()
17
+
18
+ model = 'gpt-4.1-nano'
19
+ provider = 'openai'
20
+ low_temp = 0.5
21
+ high_temp = 1.9
22
+ sample_rate = 0.4
23
+ interrupt_prob = 0.02
24
+
25
+ print(f"""
26
+ ██╗ ██╗ █████╗ ███╗ ██╗██████╗ ███████╗██████╗
27
+ ██║ ██║██╔══██╗████╗ ██║██╔══██╗██╔════╝██╔══██╗
28
+ ██║ █╗ ██║███████║██╔██╗ ██║██║ ██║█████╗ ██████╔╝
29
+ ██║███╗██║██╔══██║██║╚██╗██║██║ ██║██╔══╝ ██╔══██╗
30
+ ╚███╔███╔╝██║ ██║██║ ╚████║██████╔╝███████╗██║ ██║
31
+ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═════╝ ╚══════╝╚═╝ ╚═╝
32
+
33
+ Wandering: {problem}
34
+ """)
35
+
36
+ print(colored(f"--- Low temp stream ({low_temp}) ---", "cyan"))
37
+
38
+ low_prompt = f"Think about: {problem}"
39
+ resp = get_llm_response(low_prompt, model=model, provider=provider, temperature=low_temp, stream=True)
40
+
41
+ # Get the actual stream from the response
42
+ stream = resp.get('response') if isinstance(resp, dict) else resp
43
+
44
+ low_output = ""
45
+ interrupted = False
46
+
47
+ for chunk in stream:
48
+ if hasattr(chunk, 'choices') and chunk.choices:
49
+ delta = chunk.choices[0].delta
50
+ text = getattr(delta, 'content', '') or ''
51
+ elif isinstance(chunk, dict):
52
+ text = chunk.get('content', '') or chunk.get('response', '')
53
+ else:
54
+ text = ''
55
+
56
+ if text:
57
+ print(text, end='', flush=True)
58
+ low_output += text
59
+
60
+ if random.random() < interrupt_prob:
61
+ print(colored("\n[INTERRUPT]", "yellow"))
62
+ interrupted = True
63
+ break
64
+
65
+ print()
66
+
67
+ print(colored(f"\n--- High temp stream ({high_temp}) ---", "cyan"))
68
+
69
+ high_prompt = f"{low_output}\n\nContinue:"
70
+ resp = get_llm_response(high_prompt, model=model, provider=provider, temperature=high_temp, stream=True)
71
+ stream = resp.get('response') if isinstance(resp, dict) else resp
72
+
73
+ high_output = ""
74
+ for chunk in stream:
75
+ if hasattr(chunk, 'choices') and chunk.choices:
76
+ delta = chunk.choices[0].delta
77
+ text = getattr(delta, 'content', '') or ''
78
+ elif isinstance(chunk, dict):
79
+ text = chunk.get('content', '') or chunk.get('response', '')
80
+ else:
81
+ text = ''
82
+
83
+ if text:
84
+ print(text, end='', flush=True)
85
+ high_output += text
86
+
87
+ print()
88
+
89
+ lines = [l for l in high_output.split('\n') if l.strip()]
90
+ sample_size = max(1, int(len(lines) * sample_rate))
91
+ sampled = random.sample(lines, sample_size) if lines else [high_output]
92
+
93
+ print(colored("\n=== SAMPLED INSIGHTS ===", "yellow"))
94
+ fragments_text = chr(10).join(sampled)
95
+ print(fragments_text)
96
+
97
+ print(colored("\n=== SYNTHESIS ===", "green"))
98
+
99
+ synthesis_prompt = f"""You are a mad scientist oracle. The gibberish below contains hidden truths.
100
+
101
+ QUESTION: {problem}
102
+
103
+ CHAOS FRAGMENTS:
104
+ {fragments_text}
105
+
106
+ RULES:
107
+ 1. You MUST use AT LEAST HALF of the fragments above - quote them directly
108
+ 2. Make WILD CREATIVE LEAPS - not academic, not safe, not obvious
109
+ 3. Find patterns in the noise like reading entrails or tea leaves
110
+ 4. Foreign text, Unicode garbage, code snippets - ALL are omens with meaning
111
+ 5. Puns, wordplay, phonetic similarities - all valid connections
112
+ 6. The weirder the connection, the better
113
+ 7. NO HEDGING. No "this suggests" or "perhaps". Be BOLD. Be CERTAIN.
114
+
115
+ OUTPUT 3 WILD HYPOTHESES:
116
+ For each: Quote the fragments you're using -> Make your creative leap -> State the bold claim
117
+
118
+ These must be ideas that COULD NOT exist without this specific chaos. Surprise us. Make us see {problem} in a way nobody has before."""
119
+
120
+ resp = get_llm_response(synthesis_prompt, model=model, provider=provider, temperature=0.5, stream=True)
121
+ stream = resp.get('response') if isinstance(resp, dict) else resp
122
+
123
+ synthesis = ""
124
+ for chunk in stream:
125
+ if hasattr(chunk, 'choices') and chunk.choices:
126
+ delta = chunk.choices[0].delta
127
+ text = getattr(delta, 'content', '') or ''
128
+ elif isinstance(chunk, dict):
129
+ text = chunk.get('content', '') or chunk.get('response', '')
130
+ else:
131
+ text = ''
132
+
133
+ if text:
134
+ print(text, end='', flush=True)
135
+ synthesis += text
136
+
137
+ print()
138
+
139
+ full_output = f"""Wandering: {problem}
140
+
141
+ --- Low temp stream ({low_temp}) ---
142
+ {low_output}
143
+
144
+ --- High temp stream ({high_temp}) ---
145
+ {high_output}
146
+
147
+ === SAMPLED INSIGHTS ===
148
+ {fragments_text}
149
+
150
+ === SYNTHESIS ===
151
+ {synthesis}"""
152
+ context['output'] = full_output
@@ -0,0 +1,220 @@
1
+ jinx_name: browser_action
2
+ description: |
3
+ Perform an action in the browser. Actions:
4
+ - click: Click element
5
+ - type: Type text into element (clears first)
6
+ - type_and_enter: Type text and press Enter
7
+ - set_value: Force set value via JS (bypasses date pickers/validation)
8
+ - select: Select dropdown option by visible text
9
+ - wait: Wait for element to appear
10
+ - scroll: Scroll page (up/down/to element)
11
+ - get_text: Get text from element
12
+ - get_page: Get page title, URL, and visible text
13
+ - get_elements: Get interactive elements with their selectors
14
+ - press_key: Press a key (enter, tab, escape, etc)
15
+ Selectors: CSS (#id, .class, input[name="x"]) or xpath://... for XPath
16
+ inputs:
17
+ - action:
18
+ description: "Action: click, type, type_and_enter, set_value, select, wait, scroll, get_text, get_page, get_elements, press_key"
19
+ - selector:
20
+ description: "CSS selector or XPath (prefix xpath: for XPath)"
21
+ default: ""
22
+ - value:
23
+ description: "Value for type/select, or scroll direction, or key name"
24
+ default: ""
25
+
26
+ steps:
27
+ - name: browser_action
28
+ engine: python
29
+ code: |
30
+ from selenium.webdriver.common.by import By
31
+ from selenium.webdriver.support.ui import WebDriverWait, Select
32
+ from selenium.webdriver.support import expected_conditions as EC
33
+ from selenium.webdriver.common.keys import Keys
34
+ from npcpy.work.browser import get_current_driver
35
+
36
+ action = context.get('action', '').lower()
37
+ selector = context.get('selector', '')
38
+ value = context.get('value', '')
39
+
40
+ driver = get_current_driver()
41
+ if not driver:
42
+ output = "No active browser. Use open_browser first."
43
+ exit()
44
+
45
+ def find_element(sel):
46
+ if sel.startswith('xpath:'):
47
+ return driver.find_element(By.XPATH, sel[6:])
48
+ else:
49
+ return driver.find_element(By.CSS_SELECTOR, sel)
50
+
51
+ def wait_for_element(sel, timeout=10):
52
+ if sel.startswith('xpath:'):
53
+ return WebDriverWait(driver, timeout).until(
54
+ EC.presence_of_element_located((By.XPATH, sel[6:]))
55
+ )
56
+ else:
57
+ return WebDriverWait(driver, timeout).until(
58
+ EC.presence_of_element_located((By.CSS_SELECTOR, sel))
59
+ )
60
+
61
+ try:
62
+ if action == 'click':
63
+ elem = wait_for_element(selector)
64
+ elem.click()
65
+ output = "Clicked: {}".format(selector)
66
+
67
+ elif action == 'type':
68
+ elem = wait_for_element(selector)
69
+ elem.click() # Focus first
70
+ elem.clear()
71
+ elem.send_keys(value)
72
+ output = "Typed '{}' into {}".format(value, selector)
73
+
74
+ elif action == 'set_value':
75
+ # Force set value via JS, bypasses validation/calendars
76
+ elem = wait_for_element(selector)
77
+ driver.execute_script("arguments[0].value = arguments[1]; arguments[0].dispatchEvent(new Event('input', {bubbles: true})); arguments[0].dispatchEvent(new Event('change', {bubbles: true}));", elem, value)
78
+ output = "Set value '{}' on {}".format(value, selector)
79
+
80
+ elif action == 'type_and_enter':
81
+ elem = wait_for_element(selector)
82
+ elem.clear()
83
+ elem.send_keys(value)
84
+ elem.send_keys(Keys.RETURN)
85
+ output = "Typed '{}' and pressed Enter".format(value)
86
+
87
+ elif action == 'select':
88
+ elem = wait_for_element(selector)
89
+ select = Select(elem)
90
+ select.select_by_visible_text(value)
91
+ output = "Selected '{}' in {}".format(value, selector)
92
+
93
+ elif action == 'wait':
94
+ timeout = int(value) if value else 10
95
+ wait_for_element(selector, timeout)
96
+ output = "Element found: {}".format(selector)
97
+
98
+ elif action == 'scroll':
99
+ if value == 'down':
100
+ driver.execute_script("window.scrollBy(0, 500)")
101
+ elif value == 'up':
102
+ driver.execute_script("window.scrollBy(0, -500)")
103
+ elif selector:
104
+ elem = find_element(selector)
105
+ driver.execute_script("arguments[0].scrollIntoView();", elem)
106
+ output = "Scrolled {}".format(value or 'to element')
107
+
108
+ elif action == 'get_text':
109
+ elem = wait_for_element(selector)
110
+ output = "Text: {}".format(elem.text)
111
+
112
+ elif action == 'get_page':
113
+ title = driver.title
114
+ url = driver.current_url
115
+ body = driver.find_element(By.TAG_NAME, 'body')
116
+ text = body.text[:3000]
117
+ output = "Page: {} ({})\n\nContent:\n{}".format(title, url, text)
118
+
119
+ elif action == 'get_elements':
120
+ elements = []
121
+
122
+ def is_visible(el):
123
+ try:
124
+ return el.is_displayed() and el.size['width'] > 0
125
+ except:
126
+ return False
127
+
128
+ def safe_selector(tag, el):
129
+ eid = el.get_attribute('id')
130
+ name = el.get_attribute('name')
131
+ if eid and '.' not in eid and ' ' not in eid:
132
+ return '#' + eid
133
+ elif eid:
134
+ return '{}[id="{}"]'.format(tag, eid)
135
+ elif name:
136
+ return '{}[name="{}"]'.format(tag, name)
137
+ return None
138
+
139
+ # Get inputs
140
+ for inp in driver.find_elements(By.CSS_SELECTOR, 'input:not([type="hidden"])'):
141
+ if not is_visible(inp):
142
+ continue
143
+ sel = safe_selector('input', inp)
144
+ if not sel:
145
+ ph = inp.get_attribute('placeholder')
146
+ if ph:
147
+ sel = 'input[placeholder="{}"]'.format(ph)
148
+ else:
149
+ continue
150
+ info = {'tag': 'input', 'type': inp.get_attribute('type') or 'text', 'selector': sel}
151
+ info['placeholder'] = inp.get_attribute('placeholder') or ''
152
+ elements.append(info)
153
+
154
+ # Get buttons
155
+ for btn in driver.find_elements(By.CSS_SELECTOR, 'button, input[type="submit"], input[type="button"]'):
156
+ if not is_visible(btn):
157
+ continue
158
+ sel = safe_selector('button', btn)
159
+ if not sel and btn.text:
160
+ sel = 'xpath://button[contains(text(),"{}")]'.format(btn.text[:30])
161
+ if not sel:
162
+ continue
163
+ info = {'tag': 'button', 'selector': sel, 'text': (btn.text or '')[:50]}
164
+ elements.append(info)
165
+
166
+ # Get select dropdowns
167
+ for s in driver.find_elements(By.TAG_NAME, 'select'):
168
+ if not is_visible(s):
169
+ continue
170
+ sel = safe_selector('select', s)
171
+ if not sel:
172
+ continue
173
+ opts = [o.text for o in s.find_elements(By.TAG_NAME, 'option')[:5]]
174
+ info = {'tag': 'select', 'selector': sel, 'options': opts}
175
+ elements.append(info)
176
+
177
+ # Get links
178
+ for link in driver.find_elements(By.TAG_NAME, 'a')[:30]:
179
+ if not is_visible(link) or not link.text or len(link.text) < 2:
180
+ continue
181
+ sel = safe_selector('a', link)
182
+ if not sel:
183
+ sel = 'xpath://a[contains(text(),"{}")]'.format(link.text[:30])
184
+ info = {'tag': 'a', 'selector': sel, 'text': link.text[:50]}
185
+ elements.append(info)
186
+
187
+ output = "Found {} visible elements:\n".format(len(elements))
188
+ for el in elements[:40]:
189
+ output += "{}: {} ".format(el['tag'], el.get('selector', ''))
190
+ if el.get('text'):
191
+ output += '"{}" '.format(el['text'][:30])
192
+ if el.get('placeholder'):
193
+ output += 'placeholder="{}" '.format(el['placeholder'])
194
+ if el.get('options'):
195
+ output += "opts={} ".format(el['options'][:3])
196
+ output += "\n"
197
+
198
+ elif action == 'press_key':
199
+ key_map = {
200
+ 'enter': Keys.RETURN, 'return': Keys.RETURN,
201
+ 'tab': Keys.TAB,
202
+ 'escape': Keys.ESCAPE, 'esc': Keys.ESCAPE,
203
+ 'down': Keys.DOWN, 'up': Keys.UP,
204
+ 'left': Keys.LEFT, 'right': Keys.RIGHT,
205
+ 'backspace': Keys.BACKSPACE,
206
+ 'delete': Keys.DELETE,
207
+ }
208
+ key = key_map.get(value.lower(), value)
209
+ if selector:
210
+ elem = find_element(selector)
211
+ elem.send_keys(key)
212
+ else:
213
+ driver.find_element(By.TAG_NAME, 'body').send_keys(key)
214
+ output = "Pressed key: {}".format(value)
215
+
216
+ else:
217
+ output = "Unknown action: {}".format(action)
218
+
219
+ except Exception as e:
220
+ output = "Browser action failed: {}".format(str(e))
@@ -0,0 +1,40 @@
1
+ jinx_name: browser_screenshot
2
+ description: Take a screenshot of the current browser page.
3
+ inputs:
4
+ - filename:
5
+ description: "Optional filename for screenshot"
6
+ default: ""
7
+
8
+ steps:
9
+ - name: browser_screenshot
10
+ engine: python
11
+ code: |
12
+ import os
13
+ from datetime import datetime
14
+ from npcpy.work.browser import get_current_driver
15
+
16
+ filename = context.get('filename', '')
17
+
18
+ driver = get_current_driver()
19
+ if not driver:
20
+ output = "No active browser. Use open_browser first."
21
+ exit()
22
+
23
+ try:
24
+ screenshots_dir = os.path.expanduser('~/.npcsh/screenshots')
25
+ os.makedirs(screenshots_dir, exist_ok=True)
26
+
27
+ if not filename:
28
+ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
29
+ filename = "browser_{}.png".format(timestamp)
30
+
31
+ if not filename.endswith('.png'):
32
+ filename += '.png'
33
+
34
+ filepath = os.path.join(screenshots_dir, filename)
35
+ driver.save_screenshot(filepath)
36
+
37
+ output = "Screenshot saved: {}".format(filepath)
38
+
39
+ except Exception as e:
40
+ output = "Screenshot failed: {}".format(str(e))
@@ -0,0 +1,14 @@
1
+ jinx_name: close_browser
2
+ description: Close the current browser session.
3
+ inputs: []
4
+
5
+ steps:
6
+ - name: close_browser
7
+ engine: python
8
+ code: |
9
+ from npcpy.work.browser import close_current
10
+
11
+ if close_current():
12
+ output = "Browser closed."
13
+ else:
14
+ output = "No active browser session."
@@ -0,0 +1,43 @@
1
+ jinx_name: open_browser
2
+ description: |
3
+ Open a browser and navigate to a URL. The browser stays open for follow-up commands.
4
+ Use this to start browser automation.
5
+ inputs:
6
+ - url:
7
+ description: "URL to navigate to"
8
+ - browser: "firefox"
9
+
10
+ steps:
11
+ - name: open_browser
12
+ engine: python
13
+ code: |
14
+ from selenium import webdriver
15
+ from selenium.webdriver.firefox.service import Service as FirefoxService
16
+ from selenium.webdriver.chrome.service import Service as ChromeService
17
+ from webdriver_manager.firefox import GeckoDriverManager
18
+ from webdriver_manager.chrome import ChromeDriverManager
19
+ from npcpy.work.browser import set_driver
20
+ import uuid
21
+
22
+ url = context.get('url', '')
23
+ browser_type = context.get('browser', 'firefox').lower()
24
+
25
+ try:
26
+ if browser_type == 'chrome':
27
+ options = webdriver.ChromeOptions()
28
+ options.add_argument('--start-maximized')
29
+ driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()), options=options)
30
+ else:
31
+ options = webdriver.FirefoxOptions()
32
+ driver = webdriver.Firefox(service=FirefoxService(GeckoDriverManager().install()), options=options)
33
+ driver.maximize_window()
34
+
35
+ driver.get(url)
36
+
37
+ session_id = str(uuid.uuid4())[:8]
38
+ set_driver(session_id, driver)
39
+
40
+ output = "Browser opened. Session: {}. Navigated to: {}. Title: {}".format(session_id, url, driver.title)
41
+ except Exception as e:
42
+ import traceback
43
+ output = "Failed to open browser: {}\n{}".format(str(e), traceback.format_exc())
@@ -0,0 +1,23 @@
1
+ jinx_name: click
2
+ description: Click at screen coordinates (0-100 percentage)
3
+ inputs:
4
+ - x: 50 # X coordinate as percentage (0-100)
5
+ - y: 50 # Y coordinate as percentage (0-100)
6
+
7
+ steps:
8
+ - name: perform_click
9
+ engine: python
10
+ code: |
11
+ from npcpy.work.desktop import perform_action
12
+
13
+ x = float(context.get('x', 50))
14
+ y = float(context.get('y', 50))
15
+ messages = context.get('messages', [])
16
+
17
+ try:
18
+ perform_action({'type': 'click', 'x': x, 'y': y})
19
+ context['output'] = f"Clicked at ({x}%, {y}%)"
20
+ except Exception as e:
21
+ context['output'] = f"Click failed: {e}"
22
+
23
+ context['messages'] = messages
@@ -0,0 +1,26 @@
1
+ jinx_name: key_press
2
+ description: |
3
+ Press a keyboard key or key combination.
4
+ Valid keys: enter, tab, escape, backspace, delete, space, up, down, left, right,
5
+ home, end, pageup, pagedown, f1-f12, ctrl, alt, shift, command.
6
+ For combinations use + like: ctrl+a, ctrl+c, ctrl+v, alt+tab, ctrl+shift+t
7
+ For regular letters/numbers, use type_text instead.
8
+ inputs:
9
+ - key: "enter"
10
+
11
+ steps:
12
+ - name: perform_key
13
+ engine: python
14
+ code: |
15
+ from npcpy.work.desktop import perform_action
16
+
17
+ key = context.get('key', 'enter')
18
+ messages = context.get('messages', [])
19
+
20
+ try:
21
+ perform_action({'type': 'key', 'keys': key})
22
+ context['output'] = "Pressed key: " + str(key)
23
+ except Exception as e:
24
+ context['output'] = "Key press failed: " + str(e)
25
+
26
+ context['messages'] = messages
@@ -0,0 +1,37 @@
1
+ jinx_name: launch_app
2
+ description: Launch an application on the system
3
+ inputs:
4
+ - command: "" # Command to launch (e.g., "open -a Firefox" on macOS)
5
+
6
+ steps:
7
+ - name: perform_launch
8
+ engine: python
9
+ code: |
10
+ import platform
11
+ from npcpy.work.desktop import perform_action
12
+
13
+ command = context.get('command', '')
14
+ messages = context.get('messages', [])
15
+
16
+ if not command:
17
+ system = platform.system()
18
+ if system == "Darwin":
19
+ examples = "open -a Firefox, open -a TextEdit"
20
+ elif system == "Windows":
21
+ examples = "start firefox, notepad, calc"
22
+ else:
23
+ examples = "firefox &, gedit &"
24
+ context['output'] = f"Usage: /launch_app <command>\nExamples: {examples}"
25
+ context['messages'] = messages
26
+ exit()
27
+
28
+ try:
29
+ result = perform_action({'type': 'bash', 'command': command})
30
+ if result.get('status') == 'success':
31
+ context['output'] = f"Launched: {command}"
32
+ else:
33
+ context['output'] = f"Launch failed: {result.get('message', 'unknown error')}"
34
+ except Exception as e:
35
+ context['output'] = f"Launch failed: {e}"
36
+
37
+ context['messages'] = messages
@@ -0,0 +1,23 @@
1
+ jinx_name: screenshot
2
+ description: Capture a screenshot of the current screen
3
+ inputs:
4
+ - output_path: null # Optional path to save screenshot
5
+
6
+ steps:
7
+ - name: capture_screenshot
8
+ engine: python
9
+ code: |
10
+ from npcpy.data.image import capture_screenshot
11
+
12
+ output_path = context.get('output_path')
13
+ messages = context.get('messages', [])
14
+
15
+ result = capture_screenshot(full=True)
16
+
17
+ if result and 'file_path' in result:
18
+ context['output'] = f"Screenshot saved to: {result['file_path']}"
19
+ context['screenshot_path'] = result['file_path']
20
+ else:
21
+ context['output'] = "Failed to capture screenshot"
22
+
23
+ context['messages'] = messages