npcsh 1.1.4__py3-none-any.whl → 1.1.6__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 (102) hide show
  1. npcsh/_state.py +470 -367
  2. npcsh/npc_team/corca_example.png +0 -0
  3. npcsh/npc_team/jinxs/{python_executor.jinx → code/python.jinx} +1 -1
  4. npcsh/npc_team/jinxs/{bash_executer.jinx → code/sh.jinx} +1 -2
  5. npcsh/npc_team/jinxs/code/sql.jinx +16 -0
  6. npcsh/npc_team/jinxs/modes/alicanto.jinx +88 -0
  7. npcsh/npc_team/jinxs/modes/corca.jinx +28 -0
  8. npcsh/npc_team/jinxs/modes/guac.jinx +46 -0
  9. npcsh/npc_team/jinxs/modes/plonk.jinx +57 -0
  10. npcsh/npc_team/jinxs/modes/pti.jinx +28 -0
  11. npcsh/npc_team/jinxs/modes/spool.jinx +40 -0
  12. npcsh/npc_team/jinxs/modes/wander.jinx +81 -0
  13. npcsh/npc_team/jinxs/modes/yap.jinx +25 -0
  14. npcsh/npc_team/jinxs/utils/breathe.jinx +20 -0
  15. npcsh/npc_team/jinxs/utils/core/build.jinx +65 -0
  16. npcsh/npc_team/jinxs/utils/core/compile.jinx +50 -0
  17. npcsh/npc_team/jinxs/utils/core/help.jinx +52 -0
  18. npcsh/npc_team/jinxs/utils/core/init.jinx +41 -0
  19. npcsh/npc_team/jinxs/utils/core/jinxs.jinx +32 -0
  20. npcsh/npc_team/jinxs/utils/core/set.jinx +40 -0
  21. npcsh/npc_team/jinxs/{edit_file.jinx → utils/edit_file.jinx} +1 -1
  22. npcsh/npc_team/jinxs/utils/flush.jinx +39 -0
  23. npcsh/npc_team/jinxs/utils/npc-studio.jinx +77 -0
  24. npcsh/npc_team/jinxs/utils/ots.jinx +61 -0
  25. npcsh/npc_team/jinxs/utils/plan.jinx +33 -0
  26. npcsh/npc_team/jinxs/utils/roll.jinx +66 -0
  27. npcsh/npc_team/jinxs/utils/sample.jinx +56 -0
  28. npcsh/npc_team/jinxs/utils/search.jinx +130 -0
  29. npcsh/npc_team/jinxs/utils/serve.jinx +29 -0
  30. npcsh/npc_team/jinxs/utils/sleep.jinx +116 -0
  31. npcsh/npc_team/jinxs/utils/trigger.jinx +36 -0
  32. npcsh/npc_team/jinxs/utils/vixynt.jinx +117 -0
  33. npcsh/npcsh.py +13 -11
  34. npcsh/routes.py +97 -1419
  35. npcsh-1.1.6.data/data/npcsh/npc_team/alicanto.jinx +88 -0
  36. npcsh-1.1.6.data/data/npcsh/npc_team/breathe.jinx +20 -0
  37. npcsh-1.1.6.data/data/npcsh/npc_team/build.jinx +65 -0
  38. npcsh-1.1.6.data/data/npcsh/npc_team/compile.jinx +50 -0
  39. npcsh-1.1.6.data/data/npcsh/npc_team/corca.jinx +28 -0
  40. npcsh-1.1.6.data/data/npcsh/npc_team/corca_example.png +0 -0
  41. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/edit_file.jinx +1 -1
  42. npcsh-1.1.6.data/data/npcsh/npc_team/flush.jinx +39 -0
  43. npcsh-1.1.6.data/data/npcsh/npc_team/guac.jinx +46 -0
  44. npcsh-1.1.6.data/data/npcsh/npc_team/help.jinx +52 -0
  45. npcsh-1.1.6.data/data/npcsh/npc_team/init.jinx +41 -0
  46. npcsh-1.1.6.data/data/npcsh/npc_team/jinxs.jinx +32 -0
  47. npcsh-1.1.6.data/data/npcsh/npc_team/npc-studio.jinx +77 -0
  48. npcsh-1.1.6.data/data/npcsh/npc_team/ots.jinx +61 -0
  49. npcsh-1.1.6.data/data/npcsh/npc_team/plan.jinx +33 -0
  50. npcsh-1.1.6.data/data/npcsh/npc_team/plonk.jinx +57 -0
  51. npcsh-1.1.6.data/data/npcsh/npc_team/pti.jinx +28 -0
  52. npcsh-1.1.4.data/data/npcsh/npc_team/python_executor.jinx → npcsh-1.1.6.data/data/npcsh/npc_team/python.jinx +1 -1
  53. npcsh-1.1.6.data/data/npcsh/npc_team/roll.jinx +66 -0
  54. npcsh-1.1.6.data/data/npcsh/npc_team/sample.jinx +56 -0
  55. npcsh-1.1.6.data/data/npcsh/npc_team/search.jinx +130 -0
  56. npcsh-1.1.6.data/data/npcsh/npc_team/serve.jinx +29 -0
  57. npcsh-1.1.6.data/data/npcsh/npc_team/set.jinx +40 -0
  58. npcsh-1.1.4.data/data/npcsh/npc_team/bash_executer.jinx → npcsh-1.1.6.data/data/npcsh/npc_team/sh.jinx +1 -2
  59. npcsh-1.1.6.data/data/npcsh/npc_team/sleep.jinx +116 -0
  60. npcsh-1.1.6.data/data/npcsh/npc_team/spool.jinx +40 -0
  61. npcsh-1.1.6.data/data/npcsh/npc_team/sql.jinx +16 -0
  62. npcsh-1.1.6.data/data/npcsh/npc_team/trigger.jinx +36 -0
  63. npcsh-1.1.6.data/data/npcsh/npc_team/vixynt.jinx +117 -0
  64. npcsh-1.1.6.data/data/npcsh/npc_team/wander.jinx +81 -0
  65. npcsh-1.1.6.data/data/npcsh/npc_team/yap.jinx +25 -0
  66. {npcsh-1.1.4.dist-info → npcsh-1.1.6.dist-info}/METADATA +1 -10
  67. npcsh-1.1.6.dist-info/RECORD +124 -0
  68. npcsh/npc_team/jinxs/image_generation.jinx +0 -29
  69. npcsh/npc_team/jinxs/internet_search.jinx +0 -31
  70. npcsh/npc_team/jinxs/kg_search.jinx +0 -43
  71. npcsh/npc_team/jinxs/memory_search.jinx +0 -36
  72. npcsh/npc_team/jinxs/screen_cap.jinx +0 -25
  73. npcsh-1.1.4.data/data/npcsh/npc_team/image_generation.jinx +0 -29
  74. npcsh-1.1.4.data/data/npcsh/npc_team/internet_search.jinx +0 -31
  75. npcsh-1.1.4.data/data/npcsh/npc_team/kg_search.jinx +0 -43
  76. npcsh-1.1.4.data/data/npcsh/npc_team/memory_search.jinx +0 -36
  77. npcsh-1.1.4.data/data/npcsh/npc_team/screen_cap.jinx +0 -25
  78. npcsh-1.1.4.dist-info/RECORD +0 -78
  79. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/alicanto.npc +0 -0
  80. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/alicanto.png +0 -0
  81. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/corca.npc +0 -0
  82. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/corca.png +0 -0
  83. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/foreman.npc +0 -0
  84. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/frederic.npc +0 -0
  85. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/frederic4.png +0 -0
  86. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/guac.png +0 -0
  87. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
  88. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  89. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  90. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  91. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/plonk.npc +0 -0
  92. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/plonk.png +0 -0
  93. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/plonkjr.npc +0 -0
  94. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  95. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/sibiji.npc +0 -0
  96. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/sibiji.png +0 -0
  97. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/spool.png +0 -0
  98. {npcsh-1.1.4.data → npcsh-1.1.6.data}/data/npcsh/npc_team/yap.png +0 -0
  99. {npcsh-1.1.4.dist-info → npcsh-1.1.6.dist-info}/WHEEL +0 -0
  100. {npcsh-1.1.4.dist-info → npcsh-1.1.6.dist-info}/entry_points.txt +0 -0
  101. {npcsh-1.1.4.dist-info → npcsh-1.1.6.dist-info}/licenses/LICENSE +0 -0
  102. {npcsh-1.1.4.dist-info → npcsh-1.1.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,57 @@
1
+ jinx_name: "plonk"
2
+ description: "Use vision model to interact with GUI. Usage: /plonk <task description>"
3
+ inputs:
4
+ - task_description: "" # Required task description for GUI interaction.
5
+ - vmodel: "" # Vision model to use. Defaults to NPCSH_VISION_MODEL or NPC's model.
6
+ - vprovider: "" # Vision model provider. Defaults to NPCSH_VISION_PROVIDER or NPC's provider.
7
+ steps:
8
+ - name: "execute_plonk"
9
+ engine: "python"
10
+ code: |
11
+ import traceback
12
+ from npcsh.plonk import execute_plonk_command, format_plonk_summary
13
+ # Assuming NPCSH_VISION_MODEL and NPCSH_VISION_PROVIDER are accessible
14
+
15
+ task_description = context.get('task_description')
16
+ vision_model = context.get('vmodel')
17
+ vision_provider = context.get('vprovider')
18
+ plonk_context = context.get('plonk_context') # Passed from original context
19
+ current_npc = context.get('npc')
20
+ output_messages = context.get('messages', [])
21
+
22
+ if not task_description or not task_description.strip():
23
+ context['output'] = "Usage: /plonk <task_description> [--vmodel model_name] [--vprovider provider_name]"
24
+ context['messages'] = output_messages
25
+ exit()
26
+
27
+ # Fallback for model/provider if not explicitly set in Jinx inputs
28
+ if not vision_model and current_npc and current_npc.model:
29
+ vision_model = current_npc.model
30
+ if not vision_provider and current_npc and current_npc.provider:
31
+ vision_provider = current_npc.provider
32
+
33
+ # Final fallbacks (these would ideally come from npcsh._state config)
34
+ if not vision_model: vision_model = "gemini-1.5-pro-vision" # Example default
35
+ if not vision_provider: vision_provider = "gemini" # Example default
36
+
37
+ try:
38
+ summary_data = execute_plonk_command(
39
+ request=task_description,
40
+ model=vision_model,
41
+ provider=vision_provider,
42
+ npc=current_npc,
43
+ plonk_context=plonk_context,
44
+ debug=True # Assuming debug is often desired for plonk
45
+ )
46
+
47
+ if summary_data and isinstance(summary_data, list):
48
+ output_report = format_plonk_summary(summary_data)
49
+ context['output'] = output_report
50
+ else:
51
+ context['output'] = "Plonk command did not complete within the maximum number of iterations."
52
+
53
+ except Exception as e:
54
+ traceback.print_exc()
55
+ context['output'] = f"Error executing plonk command: {e}"
56
+
57
+ context['messages'] = output_messages
@@ -0,0 +1,28 @@
1
+ jinx_name: "pti"
2
+ description: "Enter Pardon-The-Interruption mode for human-in-the-loop reasoning."
3
+ inputs:
4
+ - command_args: "" # The full command string or specific arguments for PTI mode.
5
+ steps:
6
+ - name: "enter_pti"
7
+ engine: "python"
8
+ code: |
9
+ import traceback
10
+ from npcsh.pti import enter_pti_mode
11
+
12
+ command_args = context.get('command_args', '') # The full command string from router
13
+ output_messages = context.get('messages', [])
14
+
15
+ try:
16
+ # enter_pti_mode likely expects the full command string for its own parsing
17
+ result = enter_pti_mode(command=command_args, **context)
18
+
19
+ if isinstance(result, dict):
20
+ context['output'] = result.get('output', 'Entered PTI mode.')
21
+ context['messages'] = result.get('messages', output_messages)
22
+ else:
23
+ context['output'] = str(result)
24
+ context['messages'] = output_messages
25
+ except Exception as e:
26
+ traceback.print_exc()
27
+ context['output'] = f"Error entering pti mode: {e}"
28
+ context['messages'] = output_messages
@@ -1,4 +1,4 @@
1
- jinx_name: python_executor
1
+ jinx_name: python
2
2
  description: Execute scripts with python. You must set the ultimate result as the "output"
3
3
  variable. It MUST be a string.
4
4
  Do not add unnecessary print statements.
@@ -0,0 +1,66 @@
1
+ jinx_name: "roll"
2
+ description: "Generate a video from a text prompt."
3
+ inputs:
4
+ - prompt: "" # Required text prompt for video generation.
5
+ - num_frames: 125 # Number of frames for the video.
6
+ - width: 256 # Width of the video.
7
+ - height: 256 # Height of the video.
8
+ - output_path: "output.mp4" # Output file path for the video.
9
+ - vgmodel: "" # Video generation model to use. Defaults to NPCSH_VIDEO_GEN_MODEL or NPC's model.
10
+ - vgprovider: "" # Video generation provider to use. Defaults to NPCSH_VIDEO_GEN_PROVIDER or NPC's provider.
11
+ steps:
12
+ - name: "generate_video"
13
+ engine: "python"
14
+ code: |
15
+ import traceback
16
+ from npcpy.llm_funcs import gen_video
17
+ # Assuming NPCSH_VIDEO_GEN_MODEL and NPCSH_VIDEO_GEN_PROVIDER are accessible
18
+
19
+ prompt = context.get('prompt')
20
+ num_frames = int(context.get('num_frames', 125)) # Ensure int type
21
+ width = int(context.get('width', 256)) # Ensure int type
22
+ height = int(context.get('height', 256)) # Ensure int type
23
+ output_path = context.get('output_path')
24
+ video_gen_model = context.get('vgmodel')
25
+ video_gen_provider = context.get('vgprovider')
26
+ output_messages = context.get('messages', [])
27
+ current_npc = context.get('npc')
28
+
29
+ if not prompt or not prompt.strip():
30
+ context['output'] = "Usage: /roll <your prompt>"
31
+ context['messages'] = output_messages
32
+ exit()
33
+
34
+ # Fallback for model/provider if not explicitly set in Jinx inputs
35
+ if not video_gen_model and current_npc and current_npc.model:
36
+ video_gen_model = current_npc.model
37
+ if not video_gen_provider and current_npc and current_npc.provider:
38
+ video_gen_provider = current_npc.provider
39
+
40
+ # Final fallbacks (these would ideally come from npcsh._state config)
41
+ if not video_gen_model: video_gen_model = "stable-video-diffusion" # Example default
42
+ if not video_gen_provider: video_gen_provider = "diffusers" # Example default
43
+
44
+ try:
45
+ result = gen_video(
46
+ prompt=prompt,
47
+ model=video_gen_model,
48
+ provider=video_gen_provider,
49
+ npc=current_npc,
50
+ num_frames=num_frames,
51
+ width=width,
52
+ height=height,
53
+ output_path=output_path,
54
+ **context.get('api_kwargs', {}) # Assuming api_kwargs might be passed
55
+ )
56
+
57
+ if isinstance(result, dict):
58
+ context['output'] = result.get('output', 'Video generated.')
59
+ context['messages'] = result.get('messages', output_messages)
60
+ else:
61
+ context['output'] = str(result)
62
+ context['messages'] = output_messages
63
+ except Exception as e:
64
+ traceback.print_exc()
65
+ context['output'] = f"Error generating video: {e}"
66
+ context['messages'] = output_messages
@@ -0,0 +1,56 @@
1
+ jinx_name: "sample"
2
+ description: "Send a prompt directly to the LLM."
3
+ inputs:
4
+ - prompt: "" # Required text prompt to send to the LLM.
5
+ - model: "" # LLM model to use. Defaults to NPC's model.
6
+ - provider: "" # LLM provider to use. Defaults to NPC's provider.
7
+ steps:
8
+ - name: "send_prompt_to_llm"
9
+ engine: "python"
10
+ code: |
11
+ import traceback
12
+ from npcpy.llm_funcs import get_llm_response
13
+
14
+ prompt = context.get('prompt')
15
+ llm_model = context.get('model')
16
+ llm_provider = context.get('provider')
17
+ output_messages = context.get('messages', [])
18
+ current_npc = context.get('npc')
19
+
20
+ if not prompt or not prompt.strip():
21
+ context['output'] = "Usage: /sample <your prompt> [-m --model] model [-p --provider] provider"
22
+ context['messages'] = output_messages
23
+ exit()
24
+
25
+ # Fallback for model/provider if not explicitly set in Jinx inputs
26
+ if not llm_model and current_npc and current_npc.model:
27
+ llm_model = current_npc.model
28
+ if not llm_provider and current_npc and current_npc.provider:
29
+ llm_provider = current_npc.provider
30
+
31
+ # Final fallbacks (these would ideally come from npcsh._state config)
32
+ if not llm_model: llm_model = "gemini-1.5-pro" # Example default
33
+ if not llm_provider: llm_provider = "gemini" # Example default
34
+
35
+ try:
36
+ result = get_llm_response(
37
+ prompt=prompt,
38
+ model=llm_model,
39
+ provider=llm_provider,
40
+ npc=current_npc,
41
+ **{k:v for k,v in context.items() if k not in ['messages', 'prompt', 'model', 'provider']} # Pass other context
42
+ )
43
+
44
+ if isinstance(result, dict):
45
+ context['output'] = result.get('response')
46
+ context['messages'] = result.get('messages', output_messages)
47
+ context['model'] = llm_model
48
+ context['provider'] = llm_provider
49
+ else:
50
+ context['output'] = str(result)
51
+ context['messages'] = output_messages
52
+
53
+ except Exception as e:
54
+ traceback.print_exc()
55
+ context['output'] = f"Error sampling LLM: {e}"
56
+ context['messages'] = output_messages
@@ -0,0 +1,130 @@
1
+ jinx_name: "search"
2
+ description: >
3
+ Executes a search across various sources.
4
+ Usage:
5
+ /search <query> (Default: Web Search)
6
+ /search --memory <query> (Search approved memories)
7
+ /search --kg <query> (Search the knowledge graph)
8
+ /search --rag [-f <paths>] <query> (Execute a RAG search)
9
+ /search --brainblast <query> (Advanced history search)
10
+ inputs:
11
+ - query: ""
12
+ - memory: false
13
+ - kg: false
14
+ - rag: false
15
+ - brainblast: false
16
+ - file_paths: ""
17
+ - history_db_path: "~/npcsh_history.db"
18
+ - vector_db_path: "~/npcsh_chroma.db"
19
+ - sprovider: ""
20
+ - emodel: ""
21
+ - eprovider: ""
22
+ steps:
23
+ - name: "execute_unified_search"
24
+ engine: "python"
25
+ code: |
26
+ import os
27
+ import traceback
28
+
29
+ # Access query from context
30
+ query = context.get('query')
31
+ if not query or not query.strip():
32
+ context['output'] = "Usage: /search [--memory|--kg|--rag|--brainblast] <query>"
33
+ else:
34
+ # state is available as a GLOBAL variable (from extra_globals)
35
+ # Access it directly, not from context
36
+ try:
37
+ current_state = state # This should work now
38
+ except NameError:
39
+ context['output'] = "Error: Shell state not available in jinx context"
40
+ raise
41
+
42
+ current_npc = current_state.npc
43
+ current_team = current_state.team
44
+
45
+ npc_name = getattr(current_npc, 'name', '__none__') if current_npc else '__none__'
46
+ team_name = getattr(current_team, 'name', '__none__') if current_team else '__none__'
47
+ current_path = os.getcwd()
48
+ db_path = os.path.expanduser(context.get("history_db_path"))
49
+
50
+ try:
51
+ cmd_history = CommandHistory(db_path)
52
+
53
+ if context.get('memory'):
54
+ memories = get_relevant_memories(
55
+ command_history=cmd_history,
56
+ npc_name=npc_name,
57
+ team_name=team_name,
58
+ path=current_path,
59
+ query=query,
60
+ max_memories=10,
61
+ state=current_state # Pass the state object
62
+ )
63
+ print(memories)
64
+
65
+ if not memories:
66
+ output = f"No memories found for query: '{query}'"
67
+ else:
68
+ output = f"Found {len(memories)} memories:\n\n" + "\n".join(
69
+ f"{i}. [{mem.get('timestamp', 'unknown')}] {mem.get('final_memory') or mem.get('initial_memory')}"
70
+ for i, mem in enumerate(memories, 1)
71
+ )
72
+
73
+ elif context.get('kg'):
74
+ facts = search_kg_facts(
75
+ cmd_history,
76
+ npc_name,
77
+ team_name,
78
+ current_path,
79
+ query
80
+ )
81
+ print(facts)
82
+
83
+ if not facts:
84
+ output = f"No KG facts found for query: '{query}'"
85
+ else:
86
+ output = f"Found {len(facts)} KG facts:\n\n" + "\n".join(
87
+ f"{i}. {fact.get('statement')}" for i, fact in enumerate(facts, 1)
88
+ )
89
+
90
+ elif context.get('rag'):
91
+ file_paths_str = context.get('file_paths', '')
92
+ file_paths = [os.path.abspath(os.path.expanduser(p.strip())) for p in file_paths_str.split(',') if p.strip()]
93
+ emodel = context.get('emodel') or current_state.embedding_model
94
+ eprovider = context.get('eprovider') or current_state.embedding_provider
95
+
96
+ file_contents = []
97
+ for path in file_paths:
98
+ chunks = load_file_contents(path)
99
+ basename = os.path.basename(path)
100
+ file_contents.extend([f"{basename}: {chunk}" for chunk in chunks])
101
+
102
+ result = execute_rag_command(
103
+ command=query,
104
+ vector_db_path=os.path.expanduser(context.get('vector_db_path')),
105
+ embedding_model=emodel,
106
+ embedding_provider=eprovider,
107
+ file_contents=file_contents or None
108
+ )
109
+ print(result)
110
+ output = result.get('response', 'No response from RAG.')
111
+
112
+ elif context.get('brainblast'):
113
+ result = execute_brainblast_command(
114
+ command=query,
115
+ command_history=cmd_history,
116
+ **context
117
+ )
118
+ print(result)
119
+ output = result.get('output', 'Brainblast search executed.')
120
+
121
+ else:
122
+ # Default to web search
123
+ provider = context.get('sprovider') or current_state.search_provider
124
+ results = search_web(query, provider=provider)
125
+ output = "\n".join([f"- {res}" for res in results]) if results else "No web results found."
126
+
127
+ except Exception as e:
128
+ output = f"An error occurred in the search jinx: {e}\n{traceback.format_exc()}"
129
+
130
+ context['output'] = output
@@ -0,0 +1,29 @@
1
+ jinx_name: "serve"
2
+ description: "Serve an NPC Team"
3
+ inputs:
4
+ - port: 5337 # The port to run the Flask server on.
5
+ - cors: "" # Comma-separated CORS origins.
6
+ steps:
7
+ - name: "start_flask_server"
8
+ engine: "python"
9
+ code: |
10
+ from npcpy.serve import start_flask_server
11
+
12
+ port = context.get('port')
13
+ cors_str = context.get('cors')
14
+ output_messages = context.get('messages', [])
15
+
16
+ cors_origins = None
17
+ if cors_str and cors_str.strip():
18
+ cors_origins = [origin.strip() for origin in cors_str.split(",")]
19
+
20
+ # start_flask_server blocks, so this will hold the Jinx until the server is stopped.
21
+ # In a real-world scenario, you might want to run this in a separate process
22
+ # or have a non-blocking server start.
23
+ start_flask_server(
24
+ port=int(port), # Ensure port is an integer
25
+ cors_origins=cors_origins,
26
+ )
27
+
28
+ context['output'] = "NPC Team server started. Execution of this jinx will pause until the server is stopped."
29
+ context['messages'] = output_messages
@@ -0,0 +1,40 @@
1
+ jinx_name: "set"
2
+ description: "Set configuration values"
3
+ inputs:
4
+ - key: "" # The configuration key to set.
5
+ - value: "" # The value to set for the configuration key.
6
+ steps:
7
+ - name: "set_config_value"
8
+ engine: "python"
9
+ code: |
10
+ import traceback
11
+ # Assuming set_npcsh_config_value is accessible
12
+ try:
13
+ from npcsh._state import set_npcsh_config_value
14
+ except ImportError:
15
+ def set_npcsh_config_value(key, value):
16
+ print(f"Mock: Setting config '{key}' to '{value}'")
17
+ # In a real scenario, this might write to a config file or global state
18
+ pass
19
+
20
+ key = context.get('key')
21
+ value = context.get('value')
22
+ output_messages = context.get('messages', [])
23
+
24
+ output_result = ""
25
+ if not key or not value:
26
+ context['output'] = "Usage: /set <key>=<value>"
27
+ context['messages'] = output_messages
28
+ exit()
29
+
30
+ try:
31
+ set_npcsh_config_value(key, value)
32
+ output_result = f"Configuration value '{key}' set."
33
+ except NameError:
34
+ output_result = "Set function (set_npcsh_config_value) not available."
35
+ except Exception as e:
36
+ traceback.print_exc()
37
+ output_result = f"Error setting configuration '{key}': {e}"
38
+
39
+ context['output'] = output_result
40
+ context['messages'] = output_messages
@@ -1,8 +1,7 @@
1
- jinx_name: bash_executor
1
+ jinx_name: sh
2
2
  description: Execute bash queries. Should be used to grep for file contents, list directories, explore information to answer user questions more practically.
3
3
  inputs:
4
4
  - bash_command
5
- - user_request
6
5
  steps:
7
6
  - engine: python
8
7
  code: |
@@ -0,0 +1,116 @@
1
+ jinx_name: "sleep"
2
+ description: "Evolve knowledge graph. Use --dream to also run creative synthesis."
3
+ inputs:
4
+ - dream: False # Boolean flag to also run creative synthesis (dream process).
5
+ - ops: "" # Comma-separated list of operations to configure KG sleep process.
6
+ - model: "" # LLM model to use for KG evolution. Defaults to NPC's model.
7
+ - provider: "" # LLM provider to use for KG evolution. Defaults to NPC's provider.
8
+ steps:
9
+ - name: "evolve_knowledge_graph"
10
+ engine: "python"
11
+ code: |
12
+ import os
13
+ import traceback
14
+ from npcpy.memory.command_history import CommandHistory, load_kg_from_db, save_kg_to_db
15
+ from npcpy.memory.knowledge_graph import kg_sleep_process, kg_dream_process
16
+ # Assuming render_markdown is available if needed for logging progress
17
+
18
+ is_dreaming = context.get('dream')
19
+ operations_str = context.get('ops')
20
+ llm_model = context.get('model')
21
+ llm_provider = context.get('provider')
22
+ output_messages = context.get('messages', [])
23
+ current_npc = context.get('npc')
24
+ current_team = context.get('team')
25
+
26
+ operations_config = None
27
+ if operations_str and isinstance(operations_str, str):
28
+ operations_config = [op.strip() for op in operations_str.split(',')]
29
+
30
+ # Fallback for model/provider if not explicitly set in Jinx inputs
31
+ if not llm_model and current_npc and current_npc.model:
32
+ llm_model = current_npc.model
33
+ if not llm_provider and current_npc and current_npc.provider:
34
+ llm_provider = current_npc.provider
35
+
36
+ # Final fallbacks (these would ideally come from npcsh._state config)
37
+ if not llm_model: llm_model = "gemini-1.5-pro" # Example default
38
+ if not llm_provider: llm_provider = "gemini" # Example default
39
+
40
+ team_name = current_team.name if current_team else "__none__"
41
+ npc_name = current_npc.name if isinstance(current_npc, type(None).__class__) else "__none__"
42
+ current_path = os.getcwd()
43
+ scope_str = f"Team: '{team_name}', NPC: '{npc_name}', Path: '{current_path}'"
44
+
45
+ # Assume render_markdown exists
46
+ # render_markdown(f"- Checking knowledge graph for scope: {scope_str}")
47
+
48
+ command_history = None
49
+ try:
50
+ db_path = os.getenv("NPCSH_DB_PATH", os.path.expanduser("~/npcsh_history.db"))
51
+ command_history = CommandHistory(db_path)
52
+ engine = command_history.engine
53
+ except Exception as e:
54
+ context['output'] = f"Error connecting to history database for KG access: {e}"
55
+ context['messages'] = output_messages
56
+ exit()
57
+
58
+ output_result = ""
59
+ try:
60
+ current_kg = load_kg_from_db(engine, team_name, npc_name, current_path)
61
+
62
+ if not current_kg or not current_kg.get('facts'):
63
+ output_msg = f"Knowledge graph for the current scope is empty. Nothing to process.\n"
64
+ output_msg += f" - Scope Checked: {scope_str}\n\n"
65
+ 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."
66
+ context['output'] = output_msg
67
+ context['messages'] = output_messages
68
+ exit()
69
+
70
+ original_facts = len(current_kg.get('facts', []))
71
+ original_concepts = len(current_kg.get('concepts', []))
72
+
73
+ process_type = "Sleep"
74
+ ops_display = f"with operations: {operations_config}" if operations_config else "with random operations"
75
+ # render_markdown(f"- Initiating sleep process {ops_display}")
76
+
77
+ evolved_kg, _ = kg_sleep_process(
78
+ existing_kg=current_kg,
79
+ model=llm_model,
80
+ provider=llm_provider,
81
+ npc=current_npc,
82
+ operations_config=operations_config
83
+ )
84
+
85
+ if is_dreaming:
86
+ process_type += " & Dream"
87
+ # render_markdown(f"- Initiating dream process on the evolved KG...")
88
+ evolved_kg, _ = kg_dream_process(
89
+ existing_kg=evolved_kg,
90
+ model=llm_model,
91
+ provider=llm_provider,
92
+ npc=current_npc
93
+ )
94
+
95
+ save_kg_to_db(engine, evolved_kg, team_name, npc_name, current_path) # Changed conn to engine
96
+
97
+ new_facts = len(evolved_kg.get('facts', []))
98
+ new_concepts = len(evolved_kg.get('concepts', []))
99
+
100
+ output_result = f"{process_type} process complete.\n"
101
+ output_result += f"- Facts: {original_facts} -> {new_facts} ({new_facts - original_facts:+})\n"
102
+ output_result += f"- Concepts: {original_concepts} -> {new_concepts} ({new_concepts - original_concepts:+})"
103
+
104
+ print('Evolved facts:', evolved_kg.get('facts'))
105
+ print('Evolved concepts:', evolved_kg.get('concepts'))
106
+
107
+ context['output'] = output_result
108
+ context['messages'] = output_messages
109
+
110
+ except Exception as e:
111
+ traceback.print_exc()
112
+ context['output'] = f"Error during KG evolution process: {e}"
113
+ context['messages'] = output_messages
114
+ finally:
115
+ if command_history: # Check if it was successfully initialized
116
+ command_history.close()
@@ -0,0 +1,40 @@
1
+ jinx_name: "spool"
2
+ description: "Enter interactive chat (spool) mode"
3
+ inputs: [] # Spool mode typically takes its parameters directly from the environment/kwargs
4
+ steps:
5
+ - name: "enter_spool"
6
+ engine: "python"
7
+ code: |
8
+ import traceback
9
+ from npcpy.npc_compiler import NPC, Team
10
+ from npcsh.spool import enter_spool_mode
11
+
12
+ output_messages = context.get('messages', [])
13
+ current_npc = context.get('npc')
14
+ current_team = context.get('team')
15
+
16
+ try:
17
+ # Handle potential string NPC name if passed from CLI
18
+ if isinstance(current_npc, str) and current_team:
19
+ npc_name = current_npc
20
+ if npc_name in current_team.npcs:
21
+ current_npc = current_team.npcs[npc_name]
22
+ else:
23
+ context['output'] = f"Error: NPC '{npc_name}' not found in team. Available NPCs: {', '.join(current_team.npcs.keys())}"
24
+ context['messages'] = output_messages
25
+ exit()
26
+ context['npc'] = current_npc # Ensure the NPC object is updated in context
27
+
28
+ result = enter_spool_mode(**context) # Pass all context as kwargs
29
+
30
+ if isinstance(result, dict):
31
+ context['output'] = result.get('output', 'Exited Spool Mode.')
32
+ context['messages'] = result.get('messages', output_messages)
33
+ else:
34
+ context['output'] = str(result)
35
+ context['messages'] = output_messages
36
+
37
+ except Exception as e:
38
+ traceback.print_exc()
39
+ context['output'] = f"Error entering spool mode: {e}"
40
+ context['messages'] = output_messages
@@ -0,0 +1,16 @@
1
+ jinx_name: sql_executor
2
+ description: Execute queries on the ~/npcsh_history.db to pull data. The database
3
+ contains only information about conversations and other user-provided data. It does
4
+ not store any information about individual files. Avoid using percent signs unless absolutely necessary.
5
+ inputs:
6
+ - sql_query
7
+ steps:
8
+ - engine: python
9
+ code: |
10
+ import pandas as pd
11
+ query = "{{ sql_query }}"
12
+ try:
13
+ df = pd.read_sql_query(query, npc.db_conn)
14
+ except Exception as e:
15
+ df = pd.DataFrame({'Error': [str(e)]})
16
+ output = df.to_string()
@@ -0,0 +1,36 @@
1
+ jinx_name: "trigger"
2
+ description: "Execute a trigger command"
3
+ inputs:
4
+ - trigger_description: "" # Required description of the trigger to execute.
5
+ steps:
6
+ - name: "execute_trigger"
7
+ engine: "python"
8
+ code: |
9
+ import traceback
10
+ from npcpy.work.trigger import execute_trigger_command
11
+
12
+ trigger_description = context.get('trigger_description')
13
+ output_messages = context.get('messages', [])
14
+
15
+ if not trigger_description or not trigger_description.strip():
16
+ context['output'] = "Usage: /trigger <trigger_description>"
17
+ context['messages'] = output_messages
18
+ exit()
19
+
20
+ try:
21
+ # Pass all current context as kwargs to execute_trigger_command
22
+ result = execute_trigger_command(command=trigger_description, **context)
23
+
24
+ if isinstance(result, dict):
25
+ context['output'] = result.get('output', 'Trigger executed.')
26
+ context['messages'] = result.get('messages', output_messages)
27
+ else:
28
+ context['output'] = str(result)
29
+ context['messages'] = output_messages
30
+ except NameError:
31
+ context['output'] = "Trigger function (execute_trigger_command) not available."
32
+ context['messages'] = output_messages
33
+ except Exception as e:
34
+ traceback.print_exc()
35
+ context['output'] = f"Error executing trigger: {e}"
36
+ context['messages'] = output_messages