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.
Files changed (172) hide show
  1. npcsh/_state.py +272 -120
  2. npcsh/benchmark/npcsh_agent.py +77 -240
  3. npcsh/benchmark/templates/install-npcsh.sh.j2 +12 -4
  4. npcsh/config.py +5 -2
  5. npcsh/npc_team/alicanto.npc +4 -8
  6. npcsh/npc_team/corca.npc +5 -11
  7. npcsh/npc_team/frederic.npc +4 -6
  8. npcsh/npc_team/guac.npc +4 -4
  9. npcsh/npc_team/jinxs/lib/core/delegate.jinx +1 -1
  10. npcsh/npc_team/jinxs/lib/core/edit_file.jinx +1 -1
  11. npcsh/npc_team/jinxs/lib/core/sh.jinx +1 -1
  12. npcsh/npc_team/jinxs/lib/core/skill.jinx +59 -0
  13. npcsh/npc_team/jinxs/lib/utils/help.jinx +194 -10
  14. npcsh/npc_team/jinxs/lib/utils/init.jinx +528 -37
  15. npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +0 -1
  16. npcsh/npc_team/jinxs/lib/utils/serve.jinx +938 -21
  17. npcsh-1.1.22.data/data/npcsh/npc_team/config_tui.jinx → npcsh/npc_team/jinxs/modes/config.jinx +1 -1
  18. npcsh/npc_team/jinxs/modes/convene.jinx +76 -3
  19. npcsh/npc_team/jinxs/modes/crond.jinx +818 -0
  20. npcsh/npc_team/jinxs/modes/plonk.jinx +76 -14
  21. npcsh/npc_team/jinxs/modes/roll.jinx +368 -55
  22. npcsh/npc_team/jinxs/modes/skills.jinx +621 -0
  23. npcsh/npc_team/jinxs/modes/yap.jinx +504 -30
  24. npcsh/npc_team/jinxs/skills/code-review/SKILL.md +45 -0
  25. npcsh/npc_team/jinxs/skills/debugging/SKILL.md +44 -0
  26. npcsh/npc_team/jinxs/skills/git-workflow.jinx +44 -0
  27. npcsh/npc_team/kadiefa.npc +4 -5
  28. npcsh/npc_team/npcsh.ctx +16 -0
  29. npcsh/npc_team/plonk.npc +5 -9
  30. npcsh/npc_team/sibiji.npc +13 -5
  31. npcsh/npcsh.py +1 -0
  32. npcsh/routes.py +0 -4
  33. npcsh/yap.py +22 -4
  34. npcsh-1.1.23.data/data/npcsh/npc_team/SKILL.md +44 -0
  35. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.npc +4 -8
  36. npcsh/npc_team/jinxs/modes/config_tui.jinx → npcsh-1.1.23.data/data/npcsh/npc_team/config.jinx +1 -1
  37. npcsh-1.1.23.data/data/npcsh/npc_team/convene.jinx +670 -0
  38. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca.npc +5 -11
  39. npcsh-1.1.23.data/data/npcsh/npc_team/crond.jinx +818 -0
  40. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/delegate.jinx +1 -1
  41. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/edit_file.jinx +1 -1
  42. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/frederic.npc +4 -6
  43. npcsh-1.1.23.data/data/npcsh/npc_team/git-workflow.jinx +44 -0
  44. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.npc +4 -4
  45. npcsh-1.1.23.data/data/npcsh/npc_team/help.jinx +236 -0
  46. npcsh-1.1.23.data/data/npcsh/npc_team/init.jinx +532 -0
  47. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/jinxs.jinx +0 -1
  48. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kadiefa.npc +4 -5
  49. npcsh-1.1.23.data/data/npcsh/npc_team/npcsh.ctx +34 -0
  50. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.jinx +76 -14
  51. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.npc +5 -9
  52. npcsh-1.1.23.data/data/npcsh/npc_team/roll.jinx +378 -0
  53. npcsh-1.1.23.data/data/npcsh/npc_team/serve.jinx +943 -0
  54. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sh.jinx +1 -1
  55. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sibiji.npc +13 -5
  56. npcsh-1.1.23.data/data/npcsh/npc_team/skill.jinx +59 -0
  57. npcsh-1.1.23.data/data/npcsh/npc_team/skills.jinx +621 -0
  58. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/yap.jinx +504 -30
  59. {npcsh-1.1.22.dist-info → npcsh-1.1.23.dist-info}/METADATA +168 -7
  60. npcsh-1.1.23.dist-info/RECORD +216 -0
  61. npcsh/npc_team/jinxs/incognide/add_tab.jinx +0 -11
  62. npcsh/npc_team/jinxs/incognide/close_pane.jinx +0 -9
  63. npcsh/npc_team/jinxs/incognide/close_tab.jinx +0 -10
  64. npcsh/npc_team/jinxs/incognide/confirm.jinx +0 -10
  65. npcsh/npc_team/jinxs/incognide/focus_pane.jinx +0 -9
  66. npcsh/npc_team/jinxs/incognide/list_panes.jinx +0 -8
  67. npcsh/npc_team/jinxs/incognide/navigate.jinx +0 -10
  68. npcsh/npc_team/jinxs/incognide/notify.jinx +0 -10
  69. npcsh/npc_team/jinxs/incognide/open_pane.jinx +0 -13
  70. npcsh/npc_team/jinxs/incognide/read_pane.jinx +0 -9
  71. npcsh/npc_team/jinxs/incognide/run_terminal.jinx +0 -10
  72. npcsh/npc_team/jinxs/incognide/send_message.jinx +0 -10
  73. npcsh/npc_team/jinxs/incognide/split_pane.jinx +0 -12
  74. npcsh/npc_team/jinxs/incognide/switch_npc.jinx +0 -10
  75. npcsh/npc_team/jinxs/incognide/switch_tab.jinx +0 -10
  76. npcsh/npc_team/jinxs/incognide/write_file.jinx +0 -11
  77. npcsh/npc_team/jinxs/incognide/zen_mode.jinx +0 -9
  78. npcsh/npc_team/jinxs/lib/core/convene.jinx +0 -232
  79. npcsh-1.1.22.data/data/npcsh/npc_team/add_tab.jinx +0 -11
  80. npcsh-1.1.22.data/data/npcsh/npc_team/close_pane.jinx +0 -9
  81. npcsh-1.1.22.data/data/npcsh/npc_team/close_tab.jinx +0 -10
  82. npcsh-1.1.22.data/data/npcsh/npc_team/confirm.jinx +0 -10
  83. npcsh-1.1.22.data/data/npcsh/npc_team/convene.jinx +0 -232
  84. npcsh-1.1.22.data/data/npcsh/npc_team/focus_pane.jinx +0 -9
  85. npcsh-1.1.22.data/data/npcsh/npc_team/help.jinx +0 -52
  86. npcsh-1.1.22.data/data/npcsh/npc_team/init.jinx +0 -41
  87. npcsh-1.1.22.data/data/npcsh/npc_team/list_panes.jinx +0 -8
  88. npcsh-1.1.22.data/data/npcsh/npc_team/navigate.jinx +0 -10
  89. npcsh-1.1.22.data/data/npcsh/npc_team/notify.jinx +0 -10
  90. npcsh-1.1.22.data/data/npcsh/npc_team/npcsh.ctx +0 -18
  91. npcsh-1.1.22.data/data/npcsh/npc_team/open_pane.jinx +0 -13
  92. npcsh-1.1.22.data/data/npcsh/npc_team/read_pane.jinx +0 -9
  93. npcsh-1.1.22.data/data/npcsh/npc_team/roll.jinx +0 -65
  94. npcsh-1.1.22.data/data/npcsh/npc_team/run_terminal.jinx +0 -10
  95. npcsh-1.1.22.data/data/npcsh/npc_team/send_message.jinx +0 -10
  96. npcsh-1.1.22.data/data/npcsh/npc_team/serve.jinx +0 -26
  97. npcsh-1.1.22.data/data/npcsh/npc_team/split_pane.jinx +0 -12
  98. npcsh-1.1.22.data/data/npcsh/npc_team/switch_npc.jinx +0 -10
  99. npcsh-1.1.22.data/data/npcsh/npc_team/switch_tab.jinx +0 -10
  100. npcsh-1.1.22.data/data/npcsh/npc_team/write_file.jinx +0 -11
  101. npcsh-1.1.22.data/data/npcsh/npc_team/zen_mode.jinx +0 -9
  102. npcsh-1.1.22.dist-info/RECORD +0 -240
  103. /npcsh/npc_team/jinxs/{incognide → lib/utils}/incognide.jinx +0 -0
  104. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.jinx +0 -0
  105. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.png +0 -0
  106. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/arxiv.jinx +0 -0
  107. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/benchmark.jinx +0 -0
  108. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
  109. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
  110. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/build.jinx +0 -0
  111. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/chat.jinx +0 -0
  112. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/click.jinx +0 -0
  113. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  114. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/cmd.jinx +0 -0
  115. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/compile.jinx +0 -0
  116. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/compress.jinx +0 -0
  117. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca.jinx +0 -0
  118. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca.png +0 -0
  119. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca_example.png +0 -0
  120. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/db_search.jinx +0 -0
  121. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/file_search.jinx +0 -0
  122. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/frederic4.png +0 -0
  123. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/git.jinx +0 -0
  124. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.jinx +0 -0
  125. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.png +0 -0
  126. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/incognide.jinx +0 -0
  127. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  128. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/key_press.jinx +0 -0
  129. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kg.jinx +0 -0
  130. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
  131. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  132. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/memories.jinx +0 -0
  133. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/models.jinx +0 -0
  134. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  135. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/nql.jinx +0 -0
  136. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
  137. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/ots.jinx +0 -0
  138. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/papers.jinx +0 -0
  139. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/paste.jinx +0 -0
  140. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.png +0 -0
  141. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  142. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/pti.jinx +0 -0
  143. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/python.jinx +0 -0
  144. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/reattach.jinx +0 -0
  145. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sample.jinx +0 -0
  146. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
  147. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/set.jinx +0 -0
  148. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/setup.jinx +0 -0
  149. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/shh.jinx +0 -0
  150. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sibiji.png +0 -0
  151. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sleep.jinx +0 -0
  152. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/spool.jinx +0 -0
  153. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/spool.png +0 -0
  154. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sql.jinx +0 -0
  155. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/switch.jinx +0 -0
  156. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/switches.jinx +0 -0
  157. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sync.jinx +0 -0
  158. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/team.jinx +0 -0
  159. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
  160. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/trigger.jinx +0 -0
  161. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/type_text.jinx +0 -0
  162. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/usage.jinx +0 -0
  163. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  164. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/vixynt.jinx +0 -0
  165. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/wait.jinx +0 -0
  166. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/wander.jinx +0 -0
  167. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/web_search.jinx +0 -0
  168. {npcsh-1.1.22.data → npcsh-1.1.23.data}/data/npcsh/npc_team/yap.png +0 -0
  169. {npcsh-1.1.22.dist-info → npcsh-1.1.23.dist-info}/WHEEL +0 -0
  170. {npcsh-1.1.22.dist-info → npcsh-1.1.23.dist-info}/entry_points.txt +0 -0
  171. {npcsh-1.1.22.dist-info → npcsh-1.1.23.dist-info}/licenses/LICENSE +0 -0
  172. {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,9 +0,0 @@
1
- jinx_name: studio_zen_mode
2
- description: Toggle zen mode (fullscreen) for a 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,11 +0,0 @@
1
- jinx_name: studio_add_tab
2
- description: Add a new tab to a pane.
3
- inputs:
4
- - paneId: "active"
5
- - type: ""
6
- - path: ""
7
- steps:
8
- - name: frontend_action
9
- engine: python
10
- code: |
11
- context['output'] = "Action executed by frontend"
@@ -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,10 +0,0 @@
1
- jinx_name: studio_close_tab
2
- description: Close a specific tab in a pane.
3
- inputs:
4
- - paneId: "active"
5
- - tabIndex: 0
6
- steps:
7
- - name: frontend_action
8
- engine: python
9
- code: |
10
- context['output'] = "Action executed by frontend"
@@ -1,10 +0,0 @@
1
- jinx_name: studio_confirm
2
- description: Show a confirmation dialog and return the user's choice.
3
- inputs:
4
- - message: ""
5
- - title: "Confirm"
6
- steps:
7
- - name: frontend_action
8
- engine: python
9
- code: |
10
- 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_focus_pane
2
- description: Focus/activate a specific pane in NPC Studio.
3
- inputs:
4
- - paneId: ""
5
- steps:
6
- - name: frontend_action
7
- engine: python
8
- code: |
9
- context['output'] = "Action executed by frontend"
@@ -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
@@ -1,8 +0,0 @@
1
- jinx_name: studio_list_panes
2
- description: List all open panes in NPC Studio. Returns pane IDs, types, titles, and which is active.
3
- inputs: []
4
- steps:
5
- - name: frontend_action
6
- engine: python
7
- code: |
8
- context['output'] = "Action executed by frontend"
@@ -1,10 +0,0 @@
1
- jinx_name: studio_navigate
2
- description: Navigate a browser pane to a specific URL.
3
- inputs:
4
- - paneId: "active"
5
- - url: ""
6
- steps:
7
- - name: frontend_action
8
- engine: python
9
- code: |
10
- context['output'] = "Action executed by frontend"
@@ -1,10 +0,0 @@
1
- jinx_name: studio_notify
2
- description: Show a notification toast in NPC Studio.
3
- inputs:
4
- - message: ""
5
- - type: "info"
6
- steps:
7
- - name: frontend_action
8
- engine: python
9
- code: |
10
- context['output'] = "Action executed by frontend"