npcsh 1.1.22__py3-none-any.whl → 1.1.23__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.
- npcsh/_state.py +272 -120
- npcsh/benchmark/npcsh_agent.py +77 -240
- npcsh/benchmark/templates/install-npcsh.sh.j2 +12 -4
- npcsh/config.py +5 -2
- npcsh/npc_team/alicanto.npc +4 -8
- npcsh/npc_team/corca.npc +5 -11
- npcsh/npc_team/frederic.npc +4 -6
- npcsh/npc_team/guac.npc +4 -4
- npcsh/npc_team/jinxs/lib/core/delegate.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/edit_file.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/sh.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/skill.jinx +59 -0
- npcsh/npc_team/jinxs/lib/utils/help.jinx +194 -10
- npcsh/npc_team/jinxs/lib/utils/init.jinx +528 -37
- npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +0 -1
- npcsh/npc_team/jinxs/lib/utils/serve.jinx +938 -21
- npcsh-1.1.22.data/data/npcsh/npc_team/config_tui.jinx → npcsh/npc_team/jinxs/modes/config.jinx +1 -1
- npcsh/npc_team/jinxs/modes/convene.jinx +76 -3
- npcsh/npc_team/jinxs/modes/crond.jinx +818 -0
- npcsh/npc_team/jinxs/modes/plonk.jinx +76 -14
- npcsh/npc_team/jinxs/modes/roll.jinx +368 -55
- npcsh/npc_team/jinxs/modes/skills.jinx +621 -0
- npcsh/npc_team/jinxs/modes/yap.jinx +504 -30
- npcsh/npc_team/jinxs/skills/code-review/SKILL.md +45 -0
- npcsh/npc_team/jinxs/skills/debugging/SKILL.md +44 -0
- npcsh/npc_team/jinxs/skills/git-workflow.jinx +44 -0
- npcsh/npc_team/kadiefa.npc +4 -5
- npcsh/npc_team/npcsh.ctx +16 -0
- npcsh/npc_team/plonk.npc +5 -9
- npcsh/npc_team/sibiji.npc +13 -5
- npcsh/npcsh.py +1 -0
- npcsh/routes.py +0 -4
- npcsh/yap.py +22 -4
- npcsh-1.1.23.data/data/npcsh/npc_team/SKILL.md +44 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.npc +4 -8
- npcsh/npc_team/jinxs/modes/config_tui.jinx → npcsh-1.1.23.data/data/npcsh/npc_team/config.jinx +1 -1
- npcsh-1.1.23.data/data/npcsh/npc_team/convene.jinx +670 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca.npc +5 -11
- npcsh-1.1.23.data/data/npcsh/npc_team/crond.jinx +818 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/delegate.jinx +1 -1
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/edit_file.jinx +1 -1
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/frederic.npc +4 -6
- npcsh-1.1.23.data/data/npcsh/npc_team/git-workflow.jinx +44 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.npc +4 -4
- npcsh-1.1.23.data/data/npcsh/npc_team/help.jinx +236 -0
- npcsh-1.1.23.data/data/npcsh/npc_team/init.jinx +532 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/jinxs.jinx +0 -1
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kadiefa.npc +4 -5
- npcsh-1.1.23.data/data/npcsh/npc_team/npcsh.ctx +34 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.jinx +76 -14
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.npc +5 -9
- npcsh-1.1.23.data/data/npcsh/npc_team/roll.jinx +378 -0
- npcsh-1.1.23.data/data/npcsh/npc_team/serve.jinx +943 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sh.jinx +1 -1
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sibiji.npc +13 -5
- npcsh-1.1.23.data/data/npcsh/npc_team/skill.jinx +59 -0
- npcsh-1.1.23.data/data/npcsh/npc_team/skills.jinx +621 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/yap.jinx +504 -30
- {npcsh-1.1.22.dist-info → npcsh-1.1.23.dist-info}/METADATA +168 -7
- npcsh-1.1.23.dist-info/RECORD +216 -0
- npcsh/npc_team/jinxs/incognide/add_tab.jinx +0 -11
- npcsh/npc_team/jinxs/incognide/close_pane.jinx +0 -9
- npcsh/npc_team/jinxs/incognide/close_tab.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/confirm.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/focus_pane.jinx +0 -9
- npcsh/npc_team/jinxs/incognide/list_panes.jinx +0 -8
- npcsh/npc_team/jinxs/incognide/navigate.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/notify.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/open_pane.jinx +0 -13
- npcsh/npc_team/jinxs/incognide/read_pane.jinx +0 -9
- npcsh/npc_team/jinxs/incognide/run_terminal.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/send_message.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/split_pane.jinx +0 -12
- npcsh/npc_team/jinxs/incognide/switch_npc.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/switch_tab.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/write_file.jinx +0 -11
- npcsh/npc_team/jinxs/incognide/zen_mode.jinx +0 -9
- npcsh/npc_team/jinxs/lib/core/convene.jinx +0 -232
- npcsh-1.1.22.data/data/npcsh/npc_team/add_tab.jinx +0 -11
- npcsh-1.1.22.data/data/npcsh/npc_team/close_pane.jinx +0 -9
- npcsh-1.1.22.data/data/npcsh/npc_team/close_tab.jinx +0 -10
- npcsh-1.1.22.data/data/npcsh/npc_team/confirm.jinx +0 -10
- npcsh-1.1.22.data/data/npcsh/npc_team/convene.jinx +0 -232
- npcsh-1.1.22.data/data/npcsh/npc_team/focus_pane.jinx +0 -9
- npcsh-1.1.22.data/data/npcsh/npc_team/help.jinx +0 -52
- npcsh-1.1.22.data/data/npcsh/npc_team/init.jinx +0 -41
- npcsh-1.1.22.data/data/npcsh/npc_team/list_panes.jinx +0 -8
- npcsh-1.1.22.data/data/npcsh/npc_team/navigate.jinx +0 -10
- npcsh-1.1.22.data/data/npcsh/npc_team/notify.jinx +0 -10
- npcsh-1.1.22.data/data/npcsh/npc_team/npcsh.ctx +0 -18
- npcsh-1.1.22.data/data/npcsh/npc_team/open_pane.jinx +0 -13
- npcsh-1.1.22.data/data/npcsh/npc_team/read_pane.jinx +0 -9
- npcsh-1.1.22.data/data/npcsh/npc_team/roll.jinx +0 -65
- npcsh-1.1.22.data/data/npcsh/npc_team/run_terminal.jinx +0 -10
- npcsh-1.1.22.data/data/npcsh/npc_team/send_message.jinx +0 -10
- npcsh-1.1.22.data/data/npcsh/npc_team/serve.jinx +0 -26
- npcsh-1.1.22.data/data/npcsh/npc_team/split_pane.jinx +0 -12
- npcsh-1.1.22.data/data/npcsh/npc_team/switch_npc.jinx +0 -10
- npcsh-1.1.22.data/data/npcsh/npc_team/switch_tab.jinx +0 -10
- npcsh-1.1.22.data/data/npcsh/npc_team/write_file.jinx +0 -11
- npcsh-1.1.22.data/data/npcsh/npc_team/zen_mode.jinx +0 -9
- npcsh-1.1.22.dist-info/RECORD +0 -240
- /npcsh/npc_team/jinxs/{incognide → lib/utils}/incognide.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/arxiv.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/benchmark.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/build.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/chat.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/click.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/cmd.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/compile.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/compress.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca_example.png +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/db_search.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/file_search.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/git.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/incognide.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/key_press.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kg.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/load_file.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/memories.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/models.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/nql.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/ots.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/papers.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/paste.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/pti.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/python.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/reattach.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sample.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/set.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/setup.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/shh.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sleep.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/spool.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sql.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/switch.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/switches.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sync.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/team.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/trigger.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/type_text.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/usage.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/verbose.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/vixynt.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/wait.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/wander.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/web_search.jinx +0 -0
- {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.1.22.dist-info → npcsh-1.1.23.dist-info}/WHEEL +0 -0
- {npcsh-1.1.22.dist-info → npcsh-1.1.23.dist-info}/entry_points.txt +0 -0
- {npcsh-1.1.22.dist-info → npcsh-1.1.23.dist-info}/licenses/LICENSE +0 -0
- {npcsh-1.1.22.dist-info → npcsh-1.1.23.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
jinx_name: studio_write_file
|
|
2
|
-
description: Write content to an editor pane. Updates the file content in the pane.
|
|
3
|
-
inputs:
|
|
4
|
-
- paneId: "active"
|
|
5
|
-
- content: ""
|
|
6
|
-
- path: ""
|
|
7
|
-
steps:
|
|
8
|
-
- name: frontend_action
|
|
9
|
-
engine: python
|
|
10
|
-
code: |
|
|
11
|
-
context['output'] = "Action executed by frontend"
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
jinx_name: convene
|
|
2
|
-
description: Run a cycle of discussions between NPCs on a topic. The orchestrator convenes agents to discuss and synthesize.
|
|
3
|
-
inputs:
|
|
4
|
-
- topic: ""
|
|
5
|
-
- npcs: "alicanto,corca,guac"
|
|
6
|
-
- rounds: 3
|
|
7
|
-
- model: null
|
|
8
|
-
- provider: null
|
|
9
|
-
steps:
|
|
10
|
-
- name: convene_discussion
|
|
11
|
-
engine: python
|
|
12
|
-
code: |
|
|
13
|
-
from termcolor import colored
|
|
14
|
-
from npcpy.llm_funcs import get_llm_response
|
|
15
|
-
|
|
16
|
-
topic = context.get('topic', '')
|
|
17
|
-
npcs_str = context.get('npcs', 'alicanto,corca,guac')
|
|
18
|
-
rounds = int(context.get('rounds', 3))
|
|
19
|
-
|
|
20
|
-
npc = context.get('npc')
|
|
21
|
-
team = context.get('team')
|
|
22
|
-
messages = context.get('messages', [])
|
|
23
|
-
|
|
24
|
-
model = context.get('model') or (npc.model if npc else (state.chat_model if state else 'llama3.2'))
|
|
25
|
-
provider = context.get('provider') or (npc.provider if npc else (state.chat_provider if state else 'ollama'))
|
|
26
|
-
|
|
27
|
-
if not topic:
|
|
28
|
-
context['output'] = """Usage: /convene <topic>
|
|
29
|
-
|
|
30
|
-
Options:
|
|
31
|
-
--npcs LIST Comma-separated NPC names (default: alicanto,corca,guac)
|
|
32
|
-
--rounds N Number of discussion rounds (default: 3)
|
|
33
|
-
|
|
34
|
-
Example: /convene "How should we approach the database migration?" --npcs corca,guac,frederic
|
|
35
|
-
"""
|
|
36
|
-
exit()
|
|
37
|
-
|
|
38
|
-
npc_names = [n.strip() for n in npcs_str.split(',')]
|
|
39
|
-
|
|
40
|
-
print(f"""
|
|
41
|
-
██████ ██████ ███ ██ ██ ██ ███████ ███ ██ ███████
|
|
42
|
-
██ ██ ██ ████ ██ ██ ██ ██ ████ ██ ██
|
|
43
|
-
██ ██ ██ ██ ██ ██ ██ ██ █████ ██ ██ ██ █████
|
|
44
|
-
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
45
|
-
██████ ██████ ██ ████ ████ ███████ ██ ████ ███████
|
|
46
|
-
|
|
47
|
-
Convening Discussion
|
|
48
|
-
Topic: {topic}
|
|
49
|
-
Participants: {', '.join(npc_names)}
|
|
50
|
-
Rounds: {rounds}
|
|
51
|
-
""")
|
|
52
|
-
|
|
53
|
-
# Get NPC personas
|
|
54
|
-
participants = []
|
|
55
|
-
for name in npc_names:
|
|
56
|
-
if team and hasattr(team, 'npcs') and name in team.npcs:
|
|
57
|
-
target_npc = team.npcs[name]
|
|
58
|
-
persona = getattr(target_npc, 'primary_directive', f'{name} specialist')
|
|
59
|
-
participants.append({'name': name, 'persona': persona, 'npc': target_npc})
|
|
60
|
-
else:
|
|
61
|
-
participants.append({'name': name, 'persona': f'{name} - general assistant', 'npc': None})
|
|
62
|
-
|
|
63
|
-
import random
|
|
64
|
-
|
|
65
|
-
discussion_log = []
|
|
66
|
-
|
|
67
|
-
for round_num in range(1, rounds + 1):
|
|
68
|
-
print(colored(f"\n{'='*60}", "cyan"))
|
|
69
|
-
print(colored(f" ROUND {round_num}/{rounds}", "cyan", attrs=["bold"]))
|
|
70
|
-
print(colored(f"{'='*60}", "cyan"))
|
|
71
|
-
|
|
72
|
-
round_contributions = []
|
|
73
|
-
|
|
74
|
-
for participant in participants:
|
|
75
|
-
name = participant['name']
|
|
76
|
-
persona = participant['persona']
|
|
77
|
-
|
|
78
|
-
# Build context from previous contributions
|
|
79
|
-
prev_context = ""
|
|
80
|
-
if discussion_log:
|
|
81
|
-
prev_context = "\n\nPrevious discussion:\n"
|
|
82
|
-
for entry in discussion_log[-len(participants)*2:]:
|
|
83
|
-
prev_context += f"[{entry['speaker']}]: {entry['contribution'][:200]}...\n"
|
|
84
|
-
|
|
85
|
-
if round_contributions:
|
|
86
|
-
prev_context += "\nThis round so far:\n"
|
|
87
|
-
for entry in round_contributions:
|
|
88
|
-
prev_context += f"[{entry['speaker']}]: {entry['contribution'][:200]}...\n"
|
|
89
|
-
|
|
90
|
-
prompt = f"""You are {name}. {persona}
|
|
91
|
-
|
|
92
|
-
Topic under discussion: "{topic}"
|
|
93
|
-
{prev_context}
|
|
94
|
-
|
|
95
|
-
Provide your perspective on this topic. Be concise but insightful.
|
|
96
|
-
Build on what others have said if applicable.
|
|
97
|
-
If you disagree with something, explain why constructively.
|
|
98
|
-
"""
|
|
99
|
-
|
|
100
|
-
print(colored(f"\n[{name}]:", "yellow", attrs=["bold"]))
|
|
101
|
-
|
|
102
|
-
resp = get_llm_response(
|
|
103
|
-
prompt,
|
|
104
|
-
model=model,
|
|
105
|
-
provider=provider,
|
|
106
|
-
npc=participant.get('npc') or npc,
|
|
107
|
-
temperature=0.7
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
contribution = str(resp.get('response', ''))
|
|
111
|
-
print(contribution)
|
|
112
|
-
|
|
113
|
-
entry = {
|
|
114
|
-
'round': round_num,
|
|
115
|
-
'speaker': name,
|
|
116
|
-
'contribution': contribution
|
|
117
|
-
}
|
|
118
|
-
round_contributions.append(entry)
|
|
119
|
-
discussion_log.append(entry)
|
|
120
|
-
|
|
121
|
-
# Sample a followup from another participant
|
|
122
|
-
other_participants = [p for p in participants if p['name'] != name]
|
|
123
|
-
if other_participants:
|
|
124
|
-
followup_participant = random.choice(other_participants)
|
|
125
|
-
followup_name = followup_participant['name']
|
|
126
|
-
followup_persona = followup_participant['persona']
|
|
127
|
-
|
|
128
|
-
followup_prompt = f"""You are {followup_name}. {followup_persona}
|
|
129
|
-
|
|
130
|
-
Topic: "{topic}"
|
|
131
|
-
|
|
132
|
-
{name} just said: "{contribution[:500]}"
|
|
133
|
-
|
|
134
|
-
Respond briefly to this specific point - agree, disagree, build on it, or ask a clarifying question.
|
|
135
|
-
Keep it to 2-3 sentences.
|
|
136
|
-
"""
|
|
137
|
-
|
|
138
|
-
print(colored(f"\n [{followup_name} responds]:", "cyan"))
|
|
139
|
-
|
|
140
|
-
followup_resp = get_llm_response(
|
|
141
|
-
followup_prompt,
|
|
142
|
-
model=model,
|
|
143
|
-
provider=provider,
|
|
144
|
-
npc=followup_participant.get('npc') or npc,
|
|
145
|
-
temperature=0.7
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
followup_contribution = str(followup_resp.get('response', ''))
|
|
149
|
-
print(f" {followup_contribution}")
|
|
150
|
-
|
|
151
|
-
discussion_log.append({
|
|
152
|
-
'round': round_num,
|
|
153
|
-
'speaker': followup_name,
|
|
154
|
-
'contribution': followup_contribution,
|
|
155
|
-
'type': 'followup'
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
# Probability of original speaker responding back vs someone else
|
|
159
|
-
if random.random() < 0.4:
|
|
160
|
-
# Original speaker responds
|
|
161
|
-
responder = participant
|
|
162
|
-
responder_name = name
|
|
163
|
-
else:
|
|
164
|
-
# Sample from others (could be followup person or someone else)
|
|
165
|
-
responder = random.choice(other_participants)
|
|
166
|
-
responder_name = responder['name']
|
|
167
|
-
|
|
168
|
-
if random.random() < 0.6: # 60% chance of a counter-response
|
|
169
|
-
counter_prompt = f"""You are {responder_name}. {responder['persona']}
|
|
170
|
-
|
|
171
|
-
Topic: "{topic}"
|
|
172
|
-
|
|
173
|
-
{followup_name} responded: "{followup_contribution}"
|
|
174
|
-
|
|
175
|
-
Brief reaction (1-2 sentences). Move the discussion forward.
|
|
176
|
-
"""
|
|
177
|
-
|
|
178
|
-
print(colored(f"\n [{responder_name}]:", "magenta"))
|
|
179
|
-
|
|
180
|
-
counter_resp = get_llm_response(
|
|
181
|
-
counter_prompt,
|
|
182
|
-
model=model,
|
|
183
|
-
provider=provider,
|
|
184
|
-
npc=responder.get('npc') or npc,
|
|
185
|
-
temperature=0.7
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
counter_contribution = str(counter_resp.get('response', ''))
|
|
189
|
-
print(f" {counter_contribution}")
|
|
190
|
-
|
|
191
|
-
discussion_log.append({
|
|
192
|
-
'round': round_num,
|
|
193
|
-
'speaker': responder_name,
|
|
194
|
-
'contribution': counter_contribution,
|
|
195
|
-
'type': 'counter'
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
# Synthesis
|
|
199
|
-
print(colored(f"\n{'='*60}", "green"))
|
|
200
|
-
print(colored(" SYNTHESIS", "green", attrs=["bold"]))
|
|
201
|
-
print(colored(f"{'='*60}", "green"))
|
|
202
|
-
|
|
203
|
-
all_contributions = "\n".join([
|
|
204
|
-
f"[{e['speaker']} - Round {e['round']}]: {e['contribution']}"
|
|
205
|
-
for e in discussion_log
|
|
206
|
-
])
|
|
207
|
-
|
|
208
|
-
synthesis_prompt = f"""As the convener of this discussion on "{topic}", synthesize the key points:
|
|
209
|
-
|
|
210
|
-
Full discussion:
|
|
211
|
-
{all_contributions}
|
|
212
|
-
|
|
213
|
-
Provide:
|
|
214
|
-
1. Key agreements and consensus points
|
|
215
|
-
2. Areas of disagreement or tension
|
|
216
|
-
3. Novel ideas that emerged
|
|
217
|
-
4. Recommended next steps or actions
|
|
218
|
-
"""
|
|
219
|
-
|
|
220
|
-
resp = get_llm_response(synthesis_prompt, model=model, provider=provider, npc=npc, temperature=0.4)
|
|
221
|
-
synthesis = str(resp.get('response', ''))
|
|
222
|
-
print(synthesis)
|
|
223
|
-
|
|
224
|
-
context['output'] = synthesis
|
|
225
|
-
context['messages'] = messages
|
|
226
|
-
context['convene_result'] = {
|
|
227
|
-
'topic': topic,
|
|
228
|
-
'participants': npc_names,
|
|
229
|
-
'rounds': rounds,
|
|
230
|
-
'discussion': discussion_log,
|
|
231
|
-
'synthesis': synthesis
|
|
232
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
jinx_name: studio_close_pane
|
|
2
|
-
description: Close a pane in NPC Studio. Use paneId="active" or omit to close the active pane.
|
|
3
|
-
inputs:
|
|
4
|
-
- paneId: "active"
|
|
5
|
-
steps:
|
|
6
|
-
- name: frontend_action
|
|
7
|
-
engine: python
|
|
8
|
-
code: |
|
|
9
|
-
context['output'] = "Action executed by frontend"
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
jinx_name: convene
|
|
2
|
-
description: Run a cycle of discussions between NPCs on a topic. The orchestrator convenes agents to discuss and synthesize.
|
|
3
|
-
inputs:
|
|
4
|
-
- topic: ""
|
|
5
|
-
- npcs: "alicanto,corca,guac"
|
|
6
|
-
- rounds: 3
|
|
7
|
-
- model: null
|
|
8
|
-
- provider: null
|
|
9
|
-
steps:
|
|
10
|
-
- name: convene_discussion
|
|
11
|
-
engine: python
|
|
12
|
-
code: |
|
|
13
|
-
from termcolor import colored
|
|
14
|
-
from npcpy.llm_funcs import get_llm_response
|
|
15
|
-
|
|
16
|
-
topic = context.get('topic', '')
|
|
17
|
-
npcs_str = context.get('npcs', 'alicanto,corca,guac')
|
|
18
|
-
rounds = int(context.get('rounds', 3))
|
|
19
|
-
|
|
20
|
-
npc = context.get('npc')
|
|
21
|
-
team = context.get('team')
|
|
22
|
-
messages = context.get('messages', [])
|
|
23
|
-
|
|
24
|
-
model = context.get('model') or (npc.model if npc else (state.chat_model if state else 'llama3.2'))
|
|
25
|
-
provider = context.get('provider') or (npc.provider if npc else (state.chat_provider if state else 'ollama'))
|
|
26
|
-
|
|
27
|
-
if not topic:
|
|
28
|
-
context['output'] = """Usage: /convene <topic>
|
|
29
|
-
|
|
30
|
-
Options:
|
|
31
|
-
--npcs LIST Comma-separated NPC names (default: alicanto,corca,guac)
|
|
32
|
-
--rounds N Number of discussion rounds (default: 3)
|
|
33
|
-
|
|
34
|
-
Example: /convene "How should we approach the database migration?" --npcs corca,guac,frederic
|
|
35
|
-
"""
|
|
36
|
-
exit()
|
|
37
|
-
|
|
38
|
-
npc_names = [n.strip() for n in npcs_str.split(',')]
|
|
39
|
-
|
|
40
|
-
print(f"""
|
|
41
|
-
██████ ██████ ███ ██ ██ ██ ███████ ███ ██ ███████
|
|
42
|
-
██ ██ ██ ████ ██ ██ ██ ██ ████ ██ ██
|
|
43
|
-
██ ██ ██ ██ ██ ██ ██ ██ █████ ██ ██ ██ █████
|
|
44
|
-
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
45
|
-
██████ ██████ ██ ████ ████ ███████ ██ ████ ███████
|
|
46
|
-
|
|
47
|
-
Convening Discussion
|
|
48
|
-
Topic: {topic}
|
|
49
|
-
Participants: {', '.join(npc_names)}
|
|
50
|
-
Rounds: {rounds}
|
|
51
|
-
""")
|
|
52
|
-
|
|
53
|
-
# Get NPC personas
|
|
54
|
-
participants = []
|
|
55
|
-
for name in npc_names:
|
|
56
|
-
if team and hasattr(team, 'npcs') and name in team.npcs:
|
|
57
|
-
target_npc = team.npcs[name]
|
|
58
|
-
persona = getattr(target_npc, 'primary_directive', f'{name} specialist')
|
|
59
|
-
participants.append({'name': name, 'persona': persona, 'npc': target_npc})
|
|
60
|
-
else:
|
|
61
|
-
participants.append({'name': name, 'persona': f'{name} - general assistant', 'npc': None})
|
|
62
|
-
|
|
63
|
-
import random
|
|
64
|
-
|
|
65
|
-
discussion_log = []
|
|
66
|
-
|
|
67
|
-
for round_num in range(1, rounds + 1):
|
|
68
|
-
print(colored(f"\n{'='*60}", "cyan"))
|
|
69
|
-
print(colored(f" ROUND {round_num}/{rounds}", "cyan", attrs=["bold"]))
|
|
70
|
-
print(colored(f"{'='*60}", "cyan"))
|
|
71
|
-
|
|
72
|
-
round_contributions = []
|
|
73
|
-
|
|
74
|
-
for participant in participants:
|
|
75
|
-
name = participant['name']
|
|
76
|
-
persona = participant['persona']
|
|
77
|
-
|
|
78
|
-
# Build context from previous contributions
|
|
79
|
-
prev_context = ""
|
|
80
|
-
if discussion_log:
|
|
81
|
-
prev_context = "\n\nPrevious discussion:\n"
|
|
82
|
-
for entry in discussion_log[-len(participants)*2:]:
|
|
83
|
-
prev_context += f"[{entry['speaker']}]: {entry['contribution'][:200]}...\n"
|
|
84
|
-
|
|
85
|
-
if round_contributions:
|
|
86
|
-
prev_context += "\nThis round so far:\n"
|
|
87
|
-
for entry in round_contributions:
|
|
88
|
-
prev_context += f"[{entry['speaker']}]: {entry['contribution'][:200]}...\n"
|
|
89
|
-
|
|
90
|
-
prompt = f"""You are {name}. {persona}
|
|
91
|
-
|
|
92
|
-
Topic under discussion: "{topic}"
|
|
93
|
-
{prev_context}
|
|
94
|
-
|
|
95
|
-
Provide your perspective on this topic. Be concise but insightful.
|
|
96
|
-
Build on what others have said if applicable.
|
|
97
|
-
If you disagree with something, explain why constructively.
|
|
98
|
-
"""
|
|
99
|
-
|
|
100
|
-
print(colored(f"\n[{name}]:", "yellow", attrs=["bold"]))
|
|
101
|
-
|
|
102
|
-
resp = get_llm_response(
|
|
103
|
-
prompt,
|
|
104
|
-
model=model,
|
|
105
|
-
provider=provider,
|
|
106
|
-
npc=participant.get('npc') or npc,
|
|
107
|
-
temperature=0.7
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
contribution = str(resp.get('response', ''))
|
|
111
|
-
print(contribution)
|
|
112
|
-
|
|
113
|
-
entry = {
|
|
114
|
-
'round': round_num,
|
|
115
|
-
'speaker': name,
|
|
116
|
-
'contribution': contribution
|
|
117
|
-
}
|
|
118
|
-
round_contributions.append(entry)
|
|
119
|
-
discussion_log.append(entry)
|
|
120
|
-
|
|
121
|
-
# Sample a followup from another participant
|
|
122
|
-
other_participants = [p for p in participants if p['name'] != name]
|
|
123
|
-
if other_participants:
|
|
124
|
-
followup_participant = random.choice(other_participants)
|
|
125
|
-
followup_name = followup_participant['name']
|
|
126
|
-
followup_persona = followup_participant['persona']
|
|
127
|
-
|
|
128
|
-
followup_prompt = f"""You are {followup_name}. {followup_persona}
|
|
129
|
-
|
|
130
|
-
Topic: "{topic}"
|
|
131
|
-
|
|
132
|
-
{name} just said: "{contribution[:500]}"
|
|
133
|
-
|
|
134
|
-
Respond briefly to this specific point - agree, disagree, build on it, or ask a clarifying question.
|
|
135
|
-
Keep it to 2-3 sentences.
|
|
136
|
-
"""
|
|
137
|
-
|
|
138
|
-
print(colored(f"\n [{followup_name} responds]:", "cyan"))
|
|
139
|
-
|
|
140
|
-
followup_resp = get_llm_response(
|
|
141
|
-
followup_prompt,
|
|
142
|
-
model=model,
|
|
143
|
-
provider=provider,
|
|
144
|
-
npc=followup_participant.get('npc') or npc,
|
|
145
|
-
temperature=0.7
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
followup_contribution = str(followup_resp.get('response', ''))
|
|
149
|
-
print(f" {followup_contribution}")
|
|
150
|
-
|
|
151
|
-
discussion_log.append({
|
|
152
|
-
'round': round_num,
|
|
153
|
-
'speaker': followup_name,
|
|
154
|
-
'contribution': followup_contribution,
|
|
155
|
-
'type': 'followup'
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
# Probability of original speaker responding back vs someone else
|
|
159
|
-
if random.random() < 0.4:
|
|
160
|
-
# Original speaker responds
|
|
161
|
-
responder = participant
|
|
162
|
-
responder_name = name
|
|
163
|
-
else:
|
|
164
|
-
# Sample from others (could be followup person or someone else)
|
|
165
|
-
responder = random.choice(other_participants)
|
|
166
|
-
responder_name = responder['name']
|
|
167
|
-
|
|
168
|
-
if random.random() < 0.6: # 60% chance of a counter-response
|
|
169
|
-
counter_prompt = f"""You are {responder_name}. {responder['persona']}
|
|
170
|
-
|
|
171
|
-
Topic: "{topic}"
|
|
172
|
-
|
|
173
|
-
{followup_name} responded: "{followup_contribution}"
|
|
174
|
-
|
|
175
|
-
Brief reaction (1-2 sentences). Move the discussion forward.
|
|
176
|
-
"""
|
|
177
|
-
|
|
178
|
-
print(colored(f"\n [{responder_name}]:", "magenta"))
|
|
179
|
-
|
|
180
|
-
counter_resp = get_llm_response(
|
|
181
|
-
counter_prompt,
|
|
182
|
-
model=model,
|
|
183
|
-
provider=provider,
|
|
184
|
-
npc=responder.get('npc') or npc,
|
|
185
|
-
temperature=0.7
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
counter_contribution = str(counter_resp.get('response', ''))
|
|
189
|
-
print(f" {counter_contribution}")
|
|
190
|
-
|
|
191
|
-
discussion_log.append({
|
|
192
|
-
'round': round_num,
|
|
193
|
-
'speaker': responder_name,
|
|
194
|
-
'contribution': counter_contribution,
|
|
195
|
-
'type': 'counter'
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
# Synthesis
|
|
199
|
-
print(colored(f"\n{'='*60}", "green"))
|
|
200
|
-
print(colored(" SYNTHESIS", "green", attrs=["bold"]))
|
|
201
|
-
print(colored(f"{'='*60}", "green"))
|
|
202
|
-
|
|
203
|
-
all_contributions = "\n".join([
|
|
204
|
-
f"[{e['speaker']} - Round {e['round']}]: {e['contribution']}"
|
|
205
|
-
for e in discussion_log
|
|
206
|
-
])
|
|
207
|
-
|
|
208
|
-
synthesis_prompt = f"""As the convener of this discussion on "{topic}", synthesize the key points:
|
|
209
|
-
|
|
210
|
-
Full discussion:
|
|
211
|
-
{all_contributions}
|
|
212
|
-
|
|
213
|
-
Provide:
|
|
214
|
-
1. Key agreements and consensus points
|
|
215
|
-
2. Areas of disagreement or tension
|
|
216
|
-
3. Novel ideas that emerged
|
|
217
|
-
4. Recommended next steps or actions
|
|
218
|
-
"""
|
|
219
|
-
|
|
220
|
-
resp = get_llm_response(synthesis_prompt, model=model, provider=provider, npc=npc, temperature=0.4)
|
|
221
|
-
synthesis = str(resp.get('response', ''))
|
|
222
|
-
print(synthesis)
|
|
223
|
-
|
|
224
|
-
context['output'] = synthesis
|
|
225
|
-
context['messages'] = messages
|
|
226
|
-
context['convene_result'] = {
|
|
227
|
-
'topic': topic,
|
|
228
|
-
'participants': npc_names,
|
|
229
|
-
'rounds': rounds,
|
|
230
|
-
'discussion': discussion_log,
|
|
231
|
-
'synthesis': synthesis
|
|
232
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
jinx_name: help
|
|
2
|
-
description: Show help for commands, NPCs, or Jinxs
|
|
3
|
-
inputs:
|
|
4
|
-
- topic: null
|
|
5
|
-
steps:
|
|
6
|
-
- name: show_help
|
|
7
|
-
engine: python
|
|
8
|
-
code: |
|
|
9
|
-
import json
|
|
10
|
-
from npcsh._state import CANONICAL_ARGS, get_argument_help
|
|
11
|
-
|
|
12
|
-
topic = context.get('topic')
|
|
13
|
-
|
|
14
|
-
if not topic:
|
|
15
|
-
output_lines = ["# Available Commands\n\n"]
|
|
16
|
-
|
|
17
|
-
all_jinxs = {}
|
|
18
|
-
if hasattr(npc, 'team') and npc.team and hasattr(npc.team, 'jinxs_dict'):
|
|
19
|
-
all_jinxs.update(npc.team.jinxs_dict)
|
|
20
|
-
if hasattr(npc, 'jinxs_dict') and npc.jinxs_dict:
|
|
21
|
-
all_jinxs.update(npc.jinxs_dict)
|
|
22
|
-
|
|
23
|
-
for cmd in sorted(all_jinxs.keys()):
|
|
24
|
-
jinx_obj = all_jinxs[cmd]
|
|
25
|
-
desc = getattr(jinx_obj, 'description', 'No description')
|
|
26
|
-
output_lines.append(f"/{cmd} - {desc}\n\n")
|
|
27
|
-
|
|
28
|
-
arg_help_map = get_argument_help()
|
|
29
|
-
if arg_help_map:
|
|
30
|
-
output_lines.append("## Common Command-Line Flags\n\n")
|
|
31
|
-
output_lines.append("The shortest unambiguous prefix works.\n")
|
|
32
|
-
|
|
33
|
-
for arg in sorted(CANONICAL_ARGS):
|
|
34
|
-
aliases = arg_help_map.get(arg, [])
|
|
35
|
-
alias_str = f"(-{min(aliases, key=len)})" if aliases else ""
|
|
36
|
-
output_lines.append(f"--{arg:<20} {alias_str}\n")
|
|
37
|
-
|
|
38
|
-
output = "".join(output_lines)
|
|
39
|
-
else:
|
|
40
|
-
jinx_obj = None
|
|
41
|
-
if hasattr(npc, 'team') and npc.team and hasattr(npc.team, 'jinxs_dict'):
|
|
42
|
-
jinx_obj = npc.team.jinxs_dict.get(topic)
|
|
43
|
-
if not jinx_obj and hasattr(npc, 'jinxs_dict'):
|
|
44
|
-
jinx_obj = npc.jinxs_dict.get(topic)
|
|
45
|
-
|
|
46
|
-
if jinx_obj:
|
|
47
|
-
output = f"## Help for Jinx: `/{topic}`\n\n"
|
|
48
|
-
output += f"- **Description**: {jinx_obj.description}\n"
|
|
49
|
-
if hasattr(jinx_obj, 'inputs') and jinx_obj.inputs:
|
|
50
|
-
output += f"- **Inputs**: {json.dumps(jinx_obj.inputs, indent=2)}\n"
|
|
51
|
-
else:
|
|
52
|
-
output = f"No help topic found for `{topic}`."
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
jinx_name: "init"
|
|
2
|
-
description: "Initialize NPC project"
|
|
3
|
-
inputs:
|
|
4
|
-
- directory: "."
|
|
5
|
-
- templates: ""
|
|
6
|
-
- context: ""
|
|
7
|
-
- model: ""
|
|
8
|
-
- provider: ""
|
|
9
|
-
steps:
|
|
10
|
-
- name: "initialize_project"
|
|
11
|
-
engine: "python"
|
|
12
|
-
code: |
|
|
13
|
-
import os
|
|
14
|
-
import traceback
|
|
15
|
-
from npcpy.npc_compiler import initialize_npc_project
|
|
16
|
-
|
|
17
|
-
directory = context.get('directory')
|
|
18
|
-
templates = context.get('templates')
|
|
19
|
-
context_param = context.get('context') # Renamed to avoid conflict with Jinx context
|
|
20
|
-
model = context.get('model')
|
|
21
|
-
provider = context.get('provider')
|
|
22
|
-
output_messages = context.get('messages', [])
|
|
23
|
-
|
|
24
|
-
output_result = ""
|
|
25
|
-
try:
|
|
26
|
-
initialize_npc_project(
|
|
27
|
-
directory=directory,
|
|
28
|
-
templates=templates,
|
|
29
|
-
context=context_param, # Use the renamed context parameter
|
|
30
|
-
model=model,
|
|
31
|
-
provider=provider
|
|
32
|
-
)
|
|
33
|
-
output_result = f"NPC project initialized in {os.path.abspath(directory)}."
|
|
34
|
-
except NameError:
|
|
35
|
-
output_result = "Init function (initialize_npc_project) not available."
|
|
36
|
-
except Exception as e:
|
|
37
|
-
traceback.print_exc()
|
|
38
|
-
output_result = f"Error initializing project: {e}"
|
|
39
|
-
|
|
40
|
-
context['output'] = output_result
|
|
41
|
-
context['messages'] = output_messages
|