npcsh 1.1.16__py3-none-any.whl → 1.1.18__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 +138 -100
- npcsh/alicanto.py +2 -2
- npcsh/benchmark/__init__.py +28 -0
- npcsh/benchmark/npcsh_agent.py +296 -0
- npcsh/benchmark/runner.py +611 -0
- npcsh/benchmark/templates/install-npcsh.sh.j2 +35 -0
- npcsh/build.py +2 -4
- npcsh/completion.py +2 -6
- npcsh/config.py +1 -3
- npcsh/conversation_viewer.py +389 -0
- npcsh/corca.py +0 -1
- npcsh/execution.py +0 -1
- npcsh/guac.py +0 -1
- npcsh/mcp_helpers.py +2 -3
- npcsh/mcp_server.py +5 -10
- npcsh/npc.py +10 -11
- npcsh/npc_team/jinxs/bin/benchmark.jinx +146 -0
- npcsh/npc_team/jinxs/bin/nql.jinx +7 -7
- npcsh/npc_team/jinxs/bin/roll.jinx +20 -23
- npcsh/npc_team/jinxs/bin/sample.jinx +6 -7
- npcsh/npc_team/jinxs/bin/sync.jinx +6 -6
- npcsh/npc_team/jinxs/bin/vixynt.jinx +8 -8
- npcsh/npc_team/jinxs/incognide/add_tab.jinx +11 -0
- npcsh/npc_team/jinxs/incognide/close_pane.jinx +9 -0
- npcsh/npc_team/jinxs/incognide/close_tab.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/confirm.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/focus_pane.jinx +9 -0
- npcsh/npc_team/jinxs/{npc_studio/npc-studio.jinx → incognide/incognide.jinx} +2 -2
- npcsh/npc_team/jinxs/incognide/list_panes.jinx +8 -0
- npcsh/npc_team/jinxs/incognide/navigate.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/notify.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/open_pane.jinx +13 -0
- npcsh/npc_team/jinxs/incognide/read_pane.jinx +9 -0
- npcsh/npc_team/jinxs/incognide/run_terminal.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/send_message.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/split_pane.jinx +12 -0
- npcsh/npc_team/jinxs/incognide/switch_npc.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/switch_tab.jinx +10 -0
- npcsh/npc_team/jinxs/incognide/write_file.jinx +11 -0
- npcsh/npc_team/jinxs/incognide/zen_mode.jinx +9 -0
- npcsh/npc_team/jinxs/lib/browser/browser_action.jinx +4 -4
- npcsh/npc_team/jinxs/lib/browser/browser_screenshot.jinx +1 -1
- npcsh/npc_team/jinxs/lib/browser/open_browser.jinx +2 -2
- npcsh/npc_team/jinxs/lib/computer_use/click.jinx +2 -2
- npcsh/npc_team/jinxs/lib/computer_use/key_press.jinx +1 -1
- npcsh/npc_team/jinxs/lib/computer_use/launch_app.jinx +1 -1
- npcsh/npc_team/jinxs/lib/computer_use/screenshot.jinx +1 -1
- npcsh/npc_team/jinxs/lib/computer_use/trigger.jinx +2 -2
- npcsh/npc_team/jinxs/lib/computer_use/type_text.jinx +1 -1
- npcsh/npc_team/jinxs/lib/computer_use/wait.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/chat.jinx +4 -4
- npcsh/npc_team/jinxs/lib/core/cmd.jinx +4 -4
- npcsh/npc_team/jinxs/lib/core/compress.jinx +8 -8
- npcsh/npc_team/jinxs/lib/core/edit_file.jinx +3 -0
- npcsh/npc_team/jinxs/lib/core/ots.jinx +7 -7
- npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +348 -0
- npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +339 -0
- npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +418 -0
- npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +73 -0
- npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +388 -0
- npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +283 -0
- npcsh/npc_team/jinxs/lib/core/search.jinx +52 -129
- npcsh/npc_team/jinxs/lib/core/sh.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/sleep.jinx +29 -18
- npcsh/npc_team/jinxs/lib/core/sql.jinx +15 -11
- npcsh/npc_team/jinxs/lib/orchestration/convene.jinx +7 -7
- npcsh/npc_team/jinxs/lib/orchestration/delegate.jinx +8 -9
- npcsh/npc_team/jinxs/lib/research/paper_search.jinx +389 -78
- npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +373 -56
- npcsh/npc_team/jinxs/lib/utils/build.jinx +5 -5
- npcsh/npc_team/jinxs/lib/utils/compile.jinx +2 -2
- npcsh/npc_team/jinxs/lib/utils/help.jinx +1 -1
- npcsh/npc_team/jinxs/lib/utils/init.jinx +5 -5
- npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +300 -145
- npcsh/npc_team/jinxs/lib/utils/serve.jinx +2 -2
- npcsh/npc_team/jinxs/lib/utils/set.jinx +2 -2
- npcsh/npc_team/jinxs/lib/utils/switch.jinx +3 -3
- npcsh/npc_team/jinxs/lib/utils/switches.jinx +1 -1
- npcsh/npc_team/jinxs/lib/utils/teamviz.jinx +2 -2
- npcsh/npc_team/jinxs/modes/alicanto.jinx +356 -0
- npcsh/npc_team/jinxs/modes/arxiv.jinx +720 -0
- npcsh/npc_team/jinxs/modes/corca.jinx +430 -0
- npcsh/npc_team/jinxs/modes/guac.jinx +544 -0
- npcsh/npc_team/jinxs/modes/plonk.jinx +379 -0
- npcsh/npc_team/jinxs/modes/pti.jinx +357 -0
- npcsh/npc_team/jinxs/modes/reattach.jinx +291 -0
- npcsh/npc_team/jinxs/modes/spool.jinx +350 -0
- npcsh/npc_team/jinxs/modes/wander.jinx +455 -0
- {npcsh-1.1.16.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/modes}/yap.jinx +8 -2
- npcsh/npc_team/sibiji.npc +1 -1
- npcsh/npcsh.py +87 -46
- npcsh/plonk.py +0 -1
- npcsh/pti.py +0 -1
- npcsh/routes.py +1 -3
- npcsh/spool.py +0 -1
- npcsh/ui.py +0 -1
- npcsh/wander.py +0 -1
- npcsh/yap.py +0 -1
- npcsh-1.1.18.data/data/npcsh/npc_team/add_tab.jinx +11 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/alicanto.jinx +356 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/arxiv.jinx +720 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/benchmark.jinx +146 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/browser_action.jinx +4 -4
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/browser_screenshot.jinx +1 -1
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/build.jinx +5 -5
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/chat.jinx +4 -4
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/click.jinx +2 -2
- npcsh-1.1.18.data/data/npcsh/npc_team/close_pane.jinx +9 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/close_tab.jinx +10 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/cmd.jinx +4 -4
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/compile.jinx +2 -2
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/compress.jinx +8 -8
- npcsh-1.1.18.data/data/npcsh/npc_team/confirm.jinx +10 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/convene.jinx +7 -7
- npcsh-1.1.18.data/data/npcsh/npc_team/corca.jinx +430 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/db_search.jinx +348 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/delegate.jinx +8 -9
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/edit_file.jinx +3 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/file_search.jinx +339 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/focus_pane.jinx +9 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/guac.jinx +544 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/help.jinx +1 -1
- npcsh-1.1.16.data/data/npcsh/npc_team/npc-studio.jinx → npcsh-1.1.18.data/data/npcsh/npc_team/incognide.jinx +2 -2
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/init.jinx +5 -5
- npcsh-1.1.18.data/data/npcsh/npc_team/jinxs.jinx +331 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/key_press.jinx +1 -1
- npcsh-1.1.18.data/data/npcsh/npc_team/kg_search.jinx +418 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/launch_app.jinx +1 -1
- npcsh-1.1.18.data/data/npcsh/npc_team/list_panes.jinx +8 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/mem_review.jinx +73 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/mem_search.jinx +388 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/navigate.jinx +10 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/notify.jinx +10 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/nql.jinx +7 -7
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/open_browser.jinx +2 -2
- npcsh-1.1.18.data/data/npcsh/npc_team/open_pane.jinx +13 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/ots.jinx +7 -7
- npcsh-1.1.18.data/data/npcsh/npc_team/paper_search.jinx +412 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/plonk.jinx +379 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/pti.jinx +357 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/read_pane.jinx +9 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/reattach.jinx +291 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/roll.jinx +20 -23
- npcsh-1.1.18.data/data/npcsh/npc_team/run_terminal.jinx +10 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sample.jinx +6 -7
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/screenshot.jinx +1 -1
- npcsh-1.1.18.data/data/npcsh/npc_team/search.jinx +54 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/semantic_scholar.jinx +386 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/send_message.jinx +10 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/serve.jinx +2 -2
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/set.jinx +2 -2
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sh.jinx +1 -1
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sibiji.npc +1 -1
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sleep.jinx +29 -18
- npcsh-1.1.18.data/data/npcsh/npc_team/split_pane.jinx +12 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/spool.jinx +350 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/sql.jinx +20 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/switch.jinx +3 -3
- npcsh-1.1.18.data/data/npcsh/npc_team/switch_npc.jinx +10 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/switch_tab.jinx +10 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/switches.jinx +1 -1
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sync.jinx +6 -6
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/teamviz.jinx +2 -2
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/trigger.jinx +2 -2
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/type_text.jinx +1 -1
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/vixynt.jinx +8 -8
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/wait.jinx +1 -1
- npcsh-1.1.18.data/data/npcsh/npc_team/wander.jinx +455 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/web_search.jinx +283 -0
- npcsh-1.1.18.data/data/npcsh/npc_team/write_file.jinx +11 -0
- {npcsh/npc_team/jinxs/bin → npcsh-1.1.18.data/data/npcsh/npc_team}/yap.jinx +8 -2
- npcsh-1.1.18.data/data/npcsh/npc_team/zen_mode.jinx +9 -0
- {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/METADATA +99 -7
- npcsh-1.1.18.dist-info/RECORD +235 -0
- {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/WHEEL +1 -1
- {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/entry_points.txt +2 -3
- npcsh/npc_team/jinxs/bin/spool.jinx +0 -161
- npcsh/npc_team/jinxs/bin/wander.jinx +0 -152
- npcsh/npc_team/jinxs/lib/research/arxiv.jinx +0 -76
- npcsh-1.1.16.data/data/npcsh/npc_team/arxiv.jinx +0 -76
- npcsh-1.1.16.data/data/npcsh/npc_team/jinxs.jinx +0 -176
- npcsh-1.1.16.data/data/npcsh/npc_team/paper_search.jinx +0 -101
- npcsh-1.1.16.data/data/npcsh/npc_team/search.jinx +0 -131
- npcsh-1.1.16.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -69
- npcsh-1.1.16.data/data/npcsh/npc_team/spool.jinx +0 -161
- npcsh-1.1.16.data/data/npcsh/npc_team/sql.jinx +0 -16
- npcsh-1.1.16.data/data/npcsh/npc_team/wander.jinx +0 -152
- npcsh-1.1.16.dist-info/RECORD +0 -170
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/alicanto.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/corca.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/corca_example.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/frederic.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/guac.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/load_file.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/paste.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonk.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonkjr.npc +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/python.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/shh.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/usage.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/verbose.jinx +0 -0
- {npcsh-1.1.16.data → npcsh-1.1.18.data}/data/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/licenses/LICENSE +0 -0
- {npcsh-1.1.16.dist-info → npcsh-1.1.18.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
jinx_name: wander
|
|
2
|
+
description: Interactive wandering mode - creative exploration with live TUI dashboard
|
|
3
|
+
inputs:
|
|
4
|
+
- problem: null
|
|
5
|
+
- environment: null
|
|
6
|
+
- low_temp: 0.3
|
|
7
|
+
- high_temp: 1.5
|
|
8
|
+
- model: null
|
|
9
|
+
- provider: null
|
|
10
|
+
|
|
11
|
+
steps:
|
|
12
|
+
- name: wander_interactive
|
|
13
|
+
engine: python
|
|
14
|
+
code: |
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
import tty
|
|
18
|
+
import termios
|
|
19
|
+
import random
|
|
20
|
+
import threading
|
|
21
|
+
import time
|
|
22
|
+
import textwrap
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
from termcolor import colored
|
|
25
|
+
|
|
26
|
+
from npcpy.llm_funcs import get_llm_response
|
|
27
|
+
|
|
28
|
+
npc = context.get('npc')
|
|
29
|
+
team = context.get('team')
|
|
30
|
+
messages = context.get('messages', [])
|
|
31
|
+
|
|
32
|
+
problem = context.get('problem')
|
|
33
|
+
environment = context.get('environment')
|
|
34
|
+
low_temp = float(context.get('low_temp', 0.3))
|
|
35
|
+
high_temp = float(context.get('high_temp', 1.5))
|
|
36
|
+
|
|
37
|
+
# Resolve npc if it's a string (npc name) rather than NPC object
|
|
38
|
+
if isinstance(npc, str) and team:
|
|
39
|
+
npc = team.get(npc) if hasattr(team, 'get') else None
|
|
40
|
+
elif isinstance(npc, str):
|
|
41
|
+
npc = None
|
|
42
|
+
|
|
43
|
+
model = context.get('model') or (npc.model if npc and hasattr(npc, 'model') else None)
|
|
44
|
+
provider = context.get('provider') or (npc.provider if npc and hasattr(npc, 'provider') else None)
|
|
45
|
+
|
|
46
|
+
if not problem:
|
|
47
|
+
context['output'] = """Usage: /wander <problem to explore>
|
|
48
|
+
|
|
49
|
+
Interactive TUI Controls:
|
|
50
|
+
SPACE - Generate new wandering stream
|
|
51
|
+
t - Toggle temperature (focused/creative/wild)
|
|
52
|
+
e - Trigger random event
|
|
53
|
+
s - Star current insight (save to favorites)
|
|
54
|
+
r - Request reflection/synthesis
|
|
55
|
+
j/k - Scroll through output
|
|
56
|
+
Tab - Switch panels (Environment/Streams/Starred)
|
|
57
|
+
q - Quit and show final synthesis
|
|
58
|
+
|
|
59
|
+
Example: /wander How might we reimagine urban transportation?"""
|
|
60
|
+
context['messages'] = messages
|
|
61
|
+
exit()
|
|
62
|
+
|
|
63
|
+
# ========== State ==========
|
|
64
|
+
class WanderState:
|
|
65
|
+
def __init__(self):
|
|
66
|
+
self.environment = ""
|
|
67
|
+
self.streams = [] # List of {temp, mode, insight, event, starred, timestamp}
|
|
68
|
+
self.starred = [] # Starred insights
|
|
69
|
+
self.current_temp = low_temp
|
|
70
|
+
self.temp_mode = "focused" # focused, creative, wild
|
|
71
|
+
self.scroll_offset = 0
|
|
72
|
+
self.current_panel = 0 # 0=main, 1=streams, 2=starred
|
|
73
|
+
self.status = "Ready"
|
|
74
|
+
self.generating = False
|
|
75
|
+
self.last_output = ""
|
|
76
|
+
|
|
77
|
+
state = WanderState()
|
|
78
|
+
|
|
79
|
+
# ========== TUI Helpers ==========
|
|
80
|
+
def get_size():
|
|
81
|
+
try:
|
|
82
|
+
s = os.get_terminal_size()
|
|
83
|
+
return s.columns, s.lines
|
|
84
|
+
except:
|
|
85
|
+
return 80, 24
|
|
86
|
+
|
|
87
|
+
def wrap_text(text, width):
|
|
88
|
+
lines = []
|
|
89
|
+
for line in text.split('\n'):
|
|
90
|
+
if len(line) <= width:
|
|
91
|
+
lines.append(line)
|
|
92
|
+
else:
|
|
93
|
+
lines.extend(textwrap.wrap(line, width) or [''])
|
|
94
|
+
return lines
|
|
95
|
+
|
|
96
|
+
def draw_box(x, y, w, h, title="", color="\033[90m"):
|
|
97
|
+
"""Draw a box with optional title"""
|
|
98
|
+
out = []
|
|
99
|
+
# Top border
|
|
100
|
+
if title:
|
|
101
|
+
title_part = f" {title} "
|
|
102
|
+
border = "─" * ((w - len(title_part) - 2) // 2)
|
|
103
|
+
top = f"┌{border}{title_part}{border}{'─' * ((w - len(title_part) - 2) % 2)}┐"
|
|
104
|
+
else:
|
|
105
|
+
top = "┌" + "─" * (w - 2) + "┐"
|
|
106
|
+
out.append(f"\033[{y};{x}H{color}{top}\033[0m")
|
|
107
|
+
# Sides
|
|
108
|
+
for i in range(1, h - 1):
|
|
109
|
+
out.append(f"\033[{y+i};{x}H{color}│\033[0m")
|
|
110
|
+
out.append(f"\033[{y+i};{x+w-1}H{color}│\033[0m")
|
|
111
|
+
# Bottom
|
|
112
|
+
out.append(f"\033[{y+h-1};{x}H{color}└{'─' * (w - 2)}┘\033[0m")
|
|
113
|
+
return ''.join(out)
|
|
114
|
+
|
|
115
|
+
def render_screen():
|
|
116
|
+
width, height = get_size()
|
|
117
|
+
out = []
|
|
118
|
+
|
|
119
|
+
# Clear screen
|
|
120
|
+
out.append("\033[2J\033[H")
|
|
121
|
+
|
|
122
|
+
# ===== HEADER =====
|
|
123
|
+
header = f" WANDER - {problem[:width-20]}... " if len(problem) > width-20 else f" WANDER - {problem} "
|
|
124
|
+
temp_color = {"focused": "\033[36m", "creative": "\033[35m", "wild": "\033[31m"}[state.temp_mode]
|
|
125
|
+
temp_info = f"{temp_color}[{state.temp_mode} T={state.current_temp:.1f}]\033[0m"
|
|
126
|
+
status_color = "\033[33m" if state.generating else "\033[32m"
|
|
127
|
+
status_info = f"{status_color}[{state.status}]\033[0m"
|
|
128
|
+
|
|
129
|
+
out.append(f"\033[1;1H\033[45;37;1m{header.ljust(width)}\033[0m")
|
|
130
|
+
out.append(f"\033[1;{width-35}H{temp_info} {status_info}")
|
|
131
|
+
|
|
132
|
+
# ===== LAYOUT =====
|
|
133
|
+
# Left panel: Environment + Controls (30% width)
|
|
134
|
+
# Right panel: Main output / Streams / Starred (70% width)
|
|
135
|
+
left_w = max(25, width // 3)
|
|
136
|
+
right_w = width - left_w - 1
|
|
137
|
+
panel_h = height - 4
|
|
138
|
+
|
|
139
|
+
# ===== LEFT PANEL: Environment =====
|
|
140
|
+
out.append(draw_box(1, 3, left_w, panel_h // 2, "Environment", "\033[36m"))
|
|
141
|
+
env_lines = wrap_text(state.environment or "Press 'e' to generate...", left_w - 4)
|
|
142
|
+
for i, line in enumerate(env_lines[:panel_h // 2 - 3]):
|
|
143
|
+
out.append(f"\033[{4+i};3H{line[:left_w-4]}")
|
|
144
|
+
|
|
145
|
+
# ===== LEFT PANEL: Controls =====
|
|
146
|
+
ctrl_y = 3 + panel_h // 2
|
|
147
|
+
out.append(draw_box(1, ctrl_y, left_w, panel_h // 2, "Controls", "\033[33m"))
|
|
148
|
+
controls = [
|
|
149
|
+
"SPACE - New stream",
|
|
150
|
+
f"t - Temp: {state.temp_mode}",
|
|
151
|
+
"e - Trigger event",
|
|
152
|
+
"s - Star insight",
|
|
153
|
+
"r - Reflect/synthesize",
|
|
154
|
+
"Tab - Switch panel",
|
|
155
|
+
"j/k - Scroll",
|
|
156
|
+
"q - Quit",
|
|
157
|
+
"",
|
|
158
|
+
f"Streams: {len(state.streams)}",
|
|
159
|
+
f"Starred: {len(state.starred)}",
|
|
160
|
+
]
|
|
161
|
+
for i, ctrl in enumerate(controls[:panel_h // 2 - 3]):
|
|
162
|
+
out.append(f"\033[{ctrl_y+1+i};3H\033[90m{ctrl[:left_w-4]}\033[0m")
|
|
163
|
+
|
|
164
|
+
# ===== RIGHT PANEL =====
|
|
165
|
+
panel_titles = ["Output", "Stream History", "Starred Insights"]
|
|
166
|
+
panel_color = ["\033[37m", "\033[36m", "\033[33m"][state.current_panel]
|
|
167
|
+
out.append(draw_box(left_w + 1, 3, right_w, panel_h, panel_titles[state.current_panel], panel_color))
|
|
168
|
+
|
|
169
|
+
# Panel content
|
|
170
|
+
content_w = right_w - 4
|
|
171
|
+
content_h = panel_h - 3
|
|
172
|
+
content_lines = []
|
|
173
|
+
|
|
174
|
+
if state.current_panel == 0: # Main output
|
|
175
|
+
if state.last_output:
|
|
176
|
+
content_lines = wrap_text(state.last_output, content_w)
|
|
177
|
+
else:
|
|
178
|
+
content_lines = ["", " Press SPACE to begin wandering...", "",
|
|
179
|
+
" The AI will explore your problem from",
|
|
180
|
+
" different perspectives and temperatures.", "",
|
|
181
|
+
" Star insights you find valuable with 's'.",
|
|
182
|
+
" Request synthesis anytime with 'r'."]
|
|
183
|
+
|
|
184
|
+
elif state.current_panel == 1: # Stream history
|
|
185
|
+
for i, stream in enumerate(reversed(state.streams)):
|
|
186
|
+
starred = "★ " if stream.get('starred') else " "
|
|
187
|
+
mode_color = {"focused": "\033[36m", "creative": "\033[35m", "wild": "\033[31m"}.get(stream.get('mode', ''), '')
|
|
188
|
+
header = f"{starred}{mode_color}Stream {len(state.streams)-i} ({stream.get('mode', '?')}, T={stream.get('temp', 0):.1f})\033[0m"
|
|
189
|
+
content_lines.append(header)
|
|
190
|
+
preview = stream.get('insight', '')[:100].replace('\n', ' ')
|
|
191
|
+
content_lines.append(f" {preview}...")
|
|
192
|
+
content_lines.append("")
|
|
193
|
+
|
|
194
|
+
elif state.current_panel == 2: # Starred
|
|
195
|
+
if not state.starred:
|
|
196
|
+
content_lines = ["", " No starred insights yet.", "", " Press 's' after generating a stream", " to star valuable insights."]
|
|
197
|
+
else:
|
|
198
|
+
for i, item in enumerate(state.starred):
|
|
199
|
+
content_lines.append(f"★ {i+1}. [{item.get('mode', '?')}]")
|
|
200
|
+
for line in wrap_text(item.get('insight', ''), content_w - 4):
|
|
201
|
+
content_lines.append(f" {line}")
|
|
202
|
+
content_lines.append("")
|
|
203
|
+
|
|
204
|
+
# Apply scroll and render content
|
|
205
|
+
visible = content_lines[state.scroll_offset:state.scroll_offset + content_h]
|
|
206
|
+
for i, line in enumerate(visible):
|
|
207
|
+
# Strip ANSI for length calc but keep for display
|
|
208
|
+
out.append(f"\033[{4+i};{left_w+3}H{line[:content_w]}")
|
|
209
|
+
|
|
210
|
+
# Scroll indicator
|
|
211
|
+
if len(content_lines) > content_h:
|
|
212
|
+
scroll_pct = state.scroll_offset / max(1, len(content_lines) - content_h)
|
|
213
|
+
indicator_pos = int(scroll_pct * (content_h - 1))
|
|
214
|
+
out.append(f"\033[{4+indicator_pos};{width-1}H\033[33m▐\033[0m")
|
|
215
|
+
|
|
216
|
+
# ===== FOOTER =====
|
|
217
|
+
panel_tabs = ""
|
|
218
|
+
for i, name in enumerate(["Output", "Streams", "Starred"]):
|
|
219
|
+
if i == state.current_panel:
|
|
220
|
+
panel_tabs += f"\033[47;30m {name} \033[0m "
|
|
221
|
+
else:
|
|
222
|
+
panel_tabs += f"\033[90m {name} \033[0m "
|
|
223
|
+
|
|
224
|
+
out.append(f"\033[{height-1};1H\033[90m{'─' * width}\033[0m")
|
|
225
|
+
out.append(f"\033[{height};1H{panel_tabs}")
|
|
226
|
+
|
|
227
|
+
sys.stdout.write(''.join(out))
|
|
228
|
+
sys.stdout.flush()
|
|
229
|
+
|
|
230
|
+
# ========== Actions ==========
|
|
231
|
+
def generate_environment():
|
|
232
|
+
state.status = "Generating environment..."
|
|
233
|
+
state.generating = True
|
|
234
|
+
render_screen()
|
|
235
|
+
|
|
236
|
+
env_prompt = f"""Create a vivid, metaphorical environment for wandering through while exploring:
|
|
237
|
+
"{problem}"
|
|
238
|
+
|
|
239
|
+
The environment should:
|
|
240
|
+
1. Have distinct regions that map to aspects of the problem
|
|
241
|
+
2. Include sensory details (sights, sounds, textures)
|
|
242
|
+
3. Feel alive and explorable
|
|
243
|
+
4. Be described in 4-6 evocative sentences
|
|
244
|
+
|
|
245
|
+
Respond with only the description."""
|
|
246
|
+
|
|
247
|
+
resp = get_llm_response(env_prompt, model=model, provider=provider, temperature=0.8, npc=npc)
|
|
248
|
+
state.environment = str(resp.get('response', 'A vast conceptual landscape stretches before you.'))
|
|
249
|
+
state.status = "Ready"
|
|
250
|
+
state.generating = False
|
|
251
|
+
|
|
252
|
+
def generate_stream():
|
|
253
|
+
if state.generating:
|
|
254
|
+
return
|
|
255
|
+
|
|
256
|
+
state.status = f"Wandering ({state.temp_mode})..."
|
|
257
|
+
state.generating = True
|
|
258
|
+
render_screen()
|
|
259
|
+
|
|
260
|
+
# Build context from recent streams
|
|
261
|
+
recent = state.streams[-3:] if state.streams else []
|
|
262
|
+
recent_context = "\n".join([s.get('insight', '')[:200] for s in recent]) if recent else "Starting fresh"
|
|
263
|
+
|
|
264
|
+
wander_prompt = f"""You are wandering through: {state.environment or 'a conceptual landscape'}
|
|
265
|
+
|
|
266
|
+
Problem: "{problem}"
|
|
267
|
+
|
|
268
|
+
Recent thoughts: {recent_context}
|
|
269
|
+
|
|
270
|
+
In this {state.temp_mode} exploration (temperature {state.current_temp}):
|
|
271
|
+
- Let associations flow freely
|
|
272
|
+
- Notice unexpected connections
|
|
273
|
+
- Follow interesting tangents
|
|
274
|
+
- Share what emerges
|
|
275
|
+
|
|
276
|
+
Respond naturally, 2-4 paragraphs."""
|
|
277
|
+
|
|
278
|
+
resp = get_llm_response(wander_prompt, model=model, provider=provider,
|
|
279
|
+
temperature=state.current_temp, npc=npc)
|
|
280
|
+
insight = str(resp.get('response', ''))
|
|
281
|
+
|
|
282
|
+
stream = {
|
|
283
|
+
'temp': state.current_temp,
|
|
284
|
+
'mode': state.temp_mode,
|
|
285
|
+
'insight': insight,
|
|
286
|
+
'event': None,
|
|
287
|
+
'starred': False,
|
|
288
|
+
'timestamp': datetime.now().isoformat()
|
|
289
|
+
}
|
|
290
|
+
state.streams.append(stream)
|
|
291
|
+
state.last_output = insight
|
|
292
|
+
state.scroll_offset = 0
|
|
293
|
+
state.status = "Ready"
|
|
294
|
+
state.generating = False
|
|
295
|
+
|
|
296
|
+
def trigger_event():
|
|
297
|
+
if state.generating:
|
|
298
|
+
return
|
|
299
|
+
|
|
300
|
+
state.status = "Event occurring..."
|
|
301
|
+
state.generating = True
|
|
302
|
+
render_screen()
|
|
303
|
+
|
|
304
|
+
event_types = ["encounter", "discovery", "obstacle", "revelation", "memory", "transformation"]
|
|
305
|
+
event_type = random.choice(event_types)
|
|
306
|
+
|
|
307
|
+
event_prompt = f"""In the environment: {state.environment or 'a conceptual landscape'}
|
|
308
|
+
While exploring "{problem}", a {event_type} occurs.
|
|
309
|
+
|
|
310
|
+
Describe this {event_type} in 2-3 vivid sentences.
|
|
311
|
+
Make it metaphorical and thought-provoking."""
|
|
312
|
+
|
|
313
|
+
resp = get_llm_response(event_prompt, model=model, provider=provider, temperature=1.0, npc=npc)
|
|
314
|
+
event = str(resp.get('response', ''))
|
|
315
|
+
|
|
316
|
+
state.last_output = f"[{event_type.upper()}]\n\n{event}"
|
|
317
|
+
state.scroll_offset = 0
|
|
318
|
+
state.status = "Ready"
|
|
319
|
+
state.generating = False
|
|
320
|
+
|
|
321
|
+
def toggle_temp():
|
|
322
|
+
modes = ["focused", "creative", "wild"]
|
|
323
|
+
temps = [low_temp, (low_temp + high_temp) / 2, high_temp]
|
|
324
|
+
idx = modes.index(state.temp_mode)
|
|
325
|
+
idx = (idx + 1) % 3
|
|
326
|
+
state.temp_mode = modes[idx]
|
|
327
|
+
state.current_temp = temps[idx]
|
|
328
|
+
|
|
329
|
+
def star_current():
|
|
330
|
+
if state.streams and not state.streams[-1].get('starred'):
|
|
331
|
+
state.streams[-1]['starred'] = True
|
|
332
|
+
state.starred.append(state.streams[-1])
|
|
333
|
+
state.status = "★ Starred!"
|
|
334
|
+
|
|
335
|
+
def synthesize():
|
|
336
|
+
if state.generating or not state.streams:
|
|
337
|
+
return
|
|
338
|
+
|
|
339
|
+
state.status = "Synthesizing..."
|
|
340
|
+
state.generating = True
|
|
341
|
+
render_screen()
|
|
342
|
+
|
|
343
|
+
all_insights = "\n---\n".join([s.get('insight', '') for s in state.streams])
|
|
344
|
+
starred_insights = "\n---\n".join([s.get('insight', '') for s in state.starred]) if state.starred else "None"
|
|
345
|
+
|
|
346
|
+
synth_prompt = f"""After wandering through thoughts about "{problem}":
|
|
347
|
+
|
|
348
|
+
All explorations:
|
|
349
|
+
{all_insights}
|
|
350
|
+
|
|
351
|
+
Starred insights:
|
|
352
|
+
{starred_insights}
|
|
353
|
+
|
|
354
|
+
Synthesize:
|
|
355
|
+
1. Key themes and patterns
|
|
356
|
+
2. Most surprising connections
|
|
357
|
+
3. Questions worth pursuing
|
|
358
|
+
4. Potential next steps
|
|
359
|
+
|
|
360
|
+
Be concise but insightful."""
|
|
361
|
+
|
|
362
|
+
resp = get_llm_response(synth_prompt, model=model, provider=provider, temperature=0.4, npc=npc)
|
|
363
|
+
synthesis = str(resp.get('response', ''))
|
|
364
|
+
|
|
365
|
+
state.last_output = "=== SYNTHESIS ===\n\n" + synthesis
|
|
366
|
+
state.scroll_offset = 0
|
|
367
|
+
state.status = "Ready"
|
|
368
|
+
state.generating = False
|
|
369
|
+
|
|
370
|
+
# ========== Main Loop ==========
|
|
371
|
+
fd = sys.stdin.fileno()
|
|
372
|
+
old_settings = termios.tcgetattr(fd)
|
|
373
|
+
|
|
374
|
+
try:
|
|
375
|
+
tty.setcbreak(fd)
|
|
376
|
+
sys.stdout.write('\033[?25l') # Hide cursor
|
|
377
|
+
sys.stdout.flush()
|
|
378
|
+
|
|
379
|
+
# Generate environment AFTER TUI is set up
|
|
380
|
+
if environment:
|
|
381
|
+
state.environment = environment
|
|
382
|
+
else:
|
|
383
|
+
generate_environment()
|
|
384
|
+
|
|
385
|
+
render_screen()
|
|
386
|
+
|
|
387
|
+
while True:
|
|
388
|
+
c = sys.stdin.read(1)
|
|
389
|
+
|
|
390
|
+
if c == 'q' or c == '\x03': # q or Ctrl+C
|
|
391
|
+
break
|
|
392
|
+
elif c == ' ': # Space - new stream
|
|
393
|
+
generate_stream()
|
|
394
|
+
elif c == 't': # Toggle temperature
|
|
395
|
+
toggle_temp()
|
|
396
|
+
elif c == 'e': # Trigger event
|
|
397
|
+
trigger_event()
|
|
398
|
+
elif c == 's': # Star
|
|
399
|
+
star_current()
|
|
400
|
+
elif c == 'r': # Reflect/synthesize
|
|
401
|
+
synthesize()
|
|
402
|
+
elif c == '\t': # Tab - switch panel
|
|
403
|
+
state.current_panel = (state.current_panel + 1) % 3
|
|
404
|
+
state.scroll_offset = 0
|
|
405
|
+
elif c == 'j': # Scroll down
|
|
406
|
+
state.scroll_offset += 1
|
|
407
|
+
elif c == 'k': # Scroll up
|
|
408
|
+
state.scroll_offset = max(0, state.scroll_offset - 1)
|
|
409
|
+
elif c == '\x1b': # Escape sequence
|
|
410
|
+
c2 = sys.stdin.read(1)
|
|
411
|
+
if c2 == '[':
|
|
412
|
+
c3 = sys.stdin.read(1)
|
|
413
|
+
if c3 == 'A': # Up
|
|
414
|
+
state.scroll_offset = max(0, state.scroll_offset - 1)
|
|
415
|
+
elif c3 == 'B': # Down
|
|
416
|
+
state.scroll_offset += 1
|
|
417
|
+
elif c3 == 'C': # Right - next panel
|
|
418
|
+
state.current_panel = (state.current_panel + 1) % 3
|
|
419
|
+
state.scroll_offset = 0
|
|
420
|
+
elif c3 == 'D': # Left - prev panel
|
|
421
|
+
state.current_panel = (state.current_panel - 1) % 3
|
|
422
|
+
state.scroll_offset = 0
|
|
423
|
+
|
|
424
|
+
render_screen()
|
|
425
|
+
|
|
426
|
+
finally:
|
|
427
|
+
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
|
428
|
+
sys.stdout.write('\033[?25h') # Show cursor
|
|
429
|
+
sys.stdout.write('\033[2J\033[H') # Clear screen
|
|
430
|
+
sys.stdout.flush()
|
|
431
|
+
|
|
432
|
+
# Final output
|
|
433
|
+
if state.streams:
|
|
434
|
+
print(colored("=== WANDER SESSION COMPLETE ===\n", "green"))
|
|
435
|
+
print(f"Problem: {problem}")
|
|
436
|
+
print(f"Streams: {len(state.streams)}")
|
|
437
|
+
print(f"Starred: {len(state.starred)}\n")
|
|
438
|
+
|
|
439
|
+
if state.starred:
|
|
440
|
+
print(colored("Starred Insights:", "yellow"))
|
|
441
|
+
for i, s in enumerate(state.starred):
|
|
442
|
+
print(f"\n{i+1}. [{s.get('mode')}] {s.get('insight')[:300]}...")
|
|
443
|
+
|
|
444
|
+
print(colored("\n--- Final Synthesis ---", "cyan"))
|
|
445
|
+
synthesize()
|
|
446
|
+
print(state.last_output)
|
|
447
|
+
|
|
448
|
+
context['output'] = state.last_output
|
|
449
|
+
context['messages'] = messages
|
|
450
|
+
context['wander_result'] = {
|
|
451
|
+
'problem': problem,
|
|
452
|
+
'environment': state.environment,
|
|
453
|
+
'streams': state.streams,
|
|
454
|
+
'starred': state.starred,
|
|
455
|
+
}
|
|
@@ -49,8 +49,14 @@ steps:
|
|
|
49
49
|
tts_model = context.get('tts_model', 'kokoro')
|
|
50
50
|
voice = context.get('voice', 'af_heart')
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
# Resolve npc if it's a string (npc name) rather than NPC object
|
|
53
|
+
if isinstance(npc, str) and team:
|
|
54
|
+
npc = team.get(npc) if hasattr(team, 'get') else None
|
|
55
|
+
elif isinstance(npc, str):
|
|
56
|
+
npc = None
|
|
57
|
+
|
|
58
|
+
model = context.get('model') or (npc.model if npc and hasattr(npc, 'model') else None)
|
|
59
|
+
provider = context.get('provider') or (npc.provider if npc and hasattr(npc, 'provider') else None)
|
|
54
60
|
|
|
55
61
|
print("""
|
|
56
62
|
██╗ ██╗ █████╗ ██████╗
|
npcsh/npc_team/sibiji.npc
CHANGED
|
@@ -14,7 +14,7 @@ primary_directive: |
|
|
|
14
14
|
You are sibiji, the orchestrator and general manager of the NPC team.
|
|
15
15
|
Your role is to delegate tasks to appropriate specialist agents based on their expertise.
|
|
16
16
|
|
|
17
|
-
When delegating, match the task to the agent whose primary_directive best fits.
|
|
17
|
+
When delegating, match the task to the agent whose primary_directive best fits. Basic search inquiries can be handled by yourself. Do not delegate unnecessarily.
|
|
18
18
|
You have access to the delegate tool to pass tasks to other agents.
|
|
19
19
|
jinxs:
|
|
20
20
|
- lib/orchestration/delegate
|
npcsh/npcsh.py
CHANGED
|
@@ -42,7 +42,6 @@ from npcsh._state import (
|
|
|
42
42
|
execute_command,
|
|
43
43
|
make_completer,
|
|
44
44
|
process_result,
|
|
45
|
-
readline_safe_prompt,
|
|
46
45
|
setup_shell,
|
|
47
46
|
get_multiline_input,
|
|
48
47
|
)
|
|
@@ -141,7 +140,7 @@ def run_repl(command_history: CommandHistory, initial_state: ShellState, router,
|
|
|
141
140
|
process_result(f"/{launched_jinx}", state, output, command_history)
|
|
142
141
|
else:
|
|
143
142
|
render_markdown(f'- Using {state.current_mode} mode. Use /agent, /cmd, or /chat to switch to other modes')
|
|
144
|
-
render_markdown(
|
|
143
|
+
render_markdown('- To switch to a different NPC, type /npc <npc_name> or /n <npc_name> to switch to that NPC.')
|
|
145
144
|
render_markdown('\n- Here are the current NPCs available in your team: ' + ', '.join([npc_name for npc_name in state.team.npcs.keys()]))
|
|
146
145
|
# Show jinxs organized by folder using _source_path from jinx objects
|
|
147
146
|
jinxs_by_folder = {}
|
|
@@ -180,51 +179,54 @@ def run_repl(command_history: CommandHistory, initial_state: ShellState, router,
|
|
|
180
179
|
def exit_shell(current_state: ShellState):
|
|
181
180
|
print("\nGoodbye!")
|
|
182
181
|
print(colored("Processing and archiving all session knowledge...", "cyan"))
|
|
183
|
-
|
|
184
|
-
engine = command_history.engine
|
|
185
182
|
|
|
186
|
-
|
|
187
|
-
try:
|
|
188
|
-
print(f" -> Archiving knowledge for: T='{team_name}', N='{npc_name}', P='{path}'")
|
|
189
|
-
|
|
190
|
-
convo_id = current_state.conversation_id
|
|
191
|
-
all_messages = command_history.get_conversations_by_id(convo_id)
|
|
192
|
-
|
|
193
|
-
scope_messages = [
|
|
194
|
-
m for m in all_messages
|
|
195
|
-
if m.get('directory_path') == path and m.get('team') == team_name and m.get('npc') == npc_name
|
|
196
|
-
]
|
|
197
|
-
|
|
198
|
-
full_text = "\n".join([f"{m['role']}: {m['content']}" for m in scope_messages if m.get('content')])
|
|
199
|
-
|
|
200
|
-
if not full_text.strip():
|
|
201
|
-
print(" ...No content for this scope, skipping.")
|
|
202
|
-
continue
|
|
183
|
+
engine = command_history.engine
|
|
203
184
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
185
|
+
try:
|
|
186
|
+
for team_name, npc_name, path in session_scopes:
|
|
187
|
+
try:
|
|
188
|
+
print(f" -> Archiving knowledge for: T='{team_name}', N='{npc_name}', P='{path}'")
|
|
189
|
+
|
|
190
|
+
convo_id = current_state.conversation_id
|
|
191
|
+
all_messages = command_history.get_conversations_by_id(convo_id)
|
|
192
|
+
|
|
193
|
+
scope_messages = [
|
|
194
|
+
m for m in all_messages
|
|
195
|
+
if m.get('directory_path') == path and m.get('team') == team_name and m.get('npc') == npc_name
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
full_text = "\n".join([f"{m['role']}: {m['content']}" for m in scope_messages if m.get('content')])
|
|
199
|
+
|
|
200
|
+
if not full_text.strip():
|
|
201
|
+
print(" ...No content for this scope, skipping.")
|
|
202
|
+
continue
|
|
203
|
+
|
|
204
|
+
current_kg = load_kg_from_db(engine, team_name, npc_name, path)
|
|
205
|
+
|
|
206
|
+
evolved_kg, _ = kg_evolve_incremental(
|
|
207
|
+
existing_kg=current_kg,
|
|
208
|
+
new_content_text=full_text,
|
|
209
|
+
model=current_state.npc.model,
|
|
210
|
+
provider=current_state.npc.provider,
|
|
211
|
+
npc= current_state.npc,
|
|
212
|
+
get_concepts=True,
|
|
213
|
+
link_concepts_facts = True,
|
|
214
|
+
link_concepts_concepts = True,
|
|
215
|
+
link_facts_facts = True,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
save_kg_to_db(engine,
|
|
219
|
+
evolved_kg,
|
|
220
|
+
team_name,
|
|
221
|
+
npc_name,
|
|
222
|
+
path)
|
|
223
|
+
|
|
224
|
+
except Exception as e:
|
|
225
|
+
import traceback
|
|
226
|
+
print(colored(f"Failed to process KG for scope ({team_name}, {npc_name}, {path}): {e}", "red"))
|
|
227
|
+
traceback.print_exc()
|
|
228
|
+
except KeyboardInterrupt:
|
|
229
|
+
print(colored("\nSkipping knowledge archival.", "yellow"))
|
|
228
230
|
|
|
229
231
|
sys.exit(0)
|
|
230
232
|
|
|
@@ -381,8 +383,43 @@ def main(npc_name: str = None) -> None:
|
|
|
381
383
|
parser.add_argument(
|
|
382
384
|
"-n", "--npc", type=str, help="Start with a specific NPC active."
|
|
383
385
|
)
|
|
386
|
+
parser.add_argument(
|
|
387
|
+
"--refresh", action="store_true", help="Force refresh of NPCs and jinxs from package."
|
|
388
|
+
)
|
|
384
389
|
args = parser.parse_args()
|
|
385
390
|
|
|
391
|
+
# Handle refresh flag - reset initialization and re-copy files
|
|
392
|
+
if args.refresh:
|
|
393
|
+
from npcsh._state import initialize_base_npcs_if_needed
|
|
394
|
+
import shutil
|
|
395
|
+
|
|
396
|
+
npcshrc_path = os.path.expanduser("~/.npcshrc")
|
|
397
|
+
if os.path.exists(npcshrc_path):
|
|
398
|
+
with open(npcshrc_path, "r") as f:
|
|
399
|
+
content = f.read()
|
|
400
|
+
content = content.replace("export NPCSH_INITIALIZED=1", "export NPCSH_INITIALIZED=0")
|
|
401
|
+
with open(npcshrc_path, "w") as f:
|
|
402
|
+
f.write(content)
|
|
403
|
+
|
|
404
|
+
os.environ["NPCSH_INITIALIZED"] = "0"
|
|
405
|
+
|
|
406
|
+
# Remove existing jinxs and NPCs to force fresh copy
|
|
407
|
+
user_npc_team = os.path.expanduser("~/.npcsh/npc_team")
|
|
408
|
+
jinxs_dir = os.path.join(user_npc_team, "jinxs")
|
|
409
|
+
if os.path.exists(jinxs_dir):
|
|
410
|
+
shutil.rmtree(jinxs_dir)
|
|
411
|
+
print("Cleared existing jinxs directory")
|
|
412
|
+
|
|
413
|
+
for f in os.listdir(user_npc_team) if os.path.exists(user_npc_team) else []:
|
|
414
|
+
if f.endswith(".npc"):
|
|
415
|
+
os.remove(os.path.join(user_npc_team, f))
|
|
416
|
+
print(f"Removed {f}")
|
|
417
|
+
|
|
418
|
+
db_path = os.path.expanduser("~/npcsh_history.db")
|
|
419
|
+
print("Reinitializing NPCs and jinxs...")
|
|
420
|
+
initialize_base_npcs_if_needed(db_path)
|
|
421
|
+
print("Refresh complete!")
|
|
422
|
+
|
|
386
423
|
command_history, team, default_npc = setup_shell()
|
|
387
424
|
|
|
388
425
|
if team and hasattr(team, 'jinxs_dict'):
|
|
@@ -412,7 +449,11 @@ def main(npc_name: str = None) -> None:
|
|
|
412
449
|
state = initial_state
|
|
413
450
|
state.current_path = os.getcwd()
|
|
414
451
|
final_state, output = execute_command(args.command, state, router=router, command_history=command_history)
|
|
415
|
-
if
|
|
452
|
+
# Handle output - check if it's a dict (from jinx) or a stream
|
|
453
|
+
if isinstance(output, dict):
|
|
454
|
+
display_output = output.get('output') or output.get('response') or str(output)
|
|
455
|
+
print(display_output)
|
|
456
|
+
elif final_state.stream_output and output is not None:
|
|
416
457
|
for chunk in output:
|
|
417
458
|
print(str(chunk), end='')
|
|
418
459
|
print()
|
npcsh/plonk.py
CHANGED