npcsh 1.1.20__py3-none-any.whl → 1.1.22__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 +15 -76
- npcsh/benchmark/npcsh_agent.py +22 -14
- npcsh/benchmark/templates/install-npcsh.sh.j2 +2 -2
- npcsh/diff_viewer.py +3 -3
- npcsh/mcp_server.py +9 -1
- npcsh/npc_team/alicanto.npc +12 -6
- npcsh/npc_team/corca.npc +0 -1
- npcsh/npc_team/frederic.npc +2 -3
- npcsh/npc_team/jinxs/lib/core/compress.jinx +373 -85
- npcsh/npc_team/jinxs/lib/core/edit_file.jinx +83 -61
- npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +17 -6
- npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +17 -6
- npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +52 -14
- npcsh/npc_team/jinxs/{bin → lib/utils}/benchmark.jinx +2 -2
- npcsh/npc_team/jinxs/{bin → lib/utils}/jinxs.jinx +12 -12
- npcsh/npc_team/jinxs/{bin → lib/utils}/models.jinx +7 -7
- npcsh/npc_team/jinxs/{bin → lib/utils}/setup.jinx +6 -6
- npcsh/npc_team/jinxs/modes/alicanto.jinx +1633 -295
- npcsh/npc_team/jinxs/modes/arxiv.jinx +5 -5
- npcsh/npc_team/jinxs/modes/build.jinx +378 -0
- npcsh/npc_team/jinxs/modes/config_tui.jinx +300 -0
- npcsh/npc_team/jinxs/modes/convene.jinx +597 -0
- npcsh/npc_team/jinxs/modes/corca.jinx +777 -387
- npcsh/npc_team/jinxs/modes/git.jinx +795 -0
- {npcsh-1.1.20.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/modes}/kg.jinx +82 -15
- npcsh/npc_team/jinxs/modes/memories.jinx +414 -0
- npcsh/npc_team/jinxs/{bin → modes}/nql.jinx +10 -21
- npcsh/npc_team/jinxs/modes/papers.jinx +578 -0
- npcsh/npc_team/jinxs/modes/plonk.jinx +503 -308
- npcsh/npc_team/jinxs/modes/reattach.jinx +3 -3
- npcsh/npc_team/jinxs/modes/spool.jinx +3 -3
- npcsh/npc_team/jinxs/{bin → modes}/team.jinx +12 -12
- npcsh/npc_team/jinxs/modes/vixynt.jinx +388 -0
- npcsh/npc_team/jinxs/modes/wander.jinx +454 -181
- npcsh/npc_team/jinxs/modes/yap.jinx +630 -182
- npcsh/npc_team/kadiefa.npc +2 -1
- npcsh/npc_team/sibiji.npc +3 -3
- npcsh/npcsh.py +112 -47
- npcsh/routes.py +4 -1
- npcsh/salmon_simulation.py +0 -0
- npcsh-1.1.22.data/data/npcsh/npc_team/alicanto.jinx +1694 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/alicanto.npc +12 -6
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/arxiv.jinx +5 -5
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/benchmark.jinx +2 -2
- npcsh-1.1.22.data/data/npcsh/npc_team/build.jinx +378 -0
- npcsh-1.1.22.data/data/npcsh/npc_team/compress.jinx +428 -0
- npcsh-1.1.22.data/data/npcsh/npc_team/config_tui.jinx +300 -0
- npcsh-1.1.22.data/data/npcsh/npc_team/corca.jinx +820 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca.npc +0 -1
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/db_search.jinx +17 -6
- npcsh-1.1.22.data/data/npcsh/npc_team/edit_file.jinx +119 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/file_search.jinx +17 -6
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/frederic.npc +2 -3
- npcsh-1.1.22.data/data/npcsh/npc_team/git.jinx +795 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/jinxs.jinx +12 -12
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/kadiefa.npc +2 -1
- {npcsh/npc_team/jinxs/bin → npcsh-1.1.22.data/data/npcsh/npc_team}/kg.jinx +82 -15
- npcsh-1.1.22.data/data/npcsh/npc_team/memories.jinx +414 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/models.jinx +7 -7
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/nql.jinx +10 -21
- npcsh-1.1.22.data/data/npcsh/npc_team/papers.jinx +578 -0
- npcsh-1.1.22.data/data/npcsh/npc_team/plonk.jinx +574 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/reattach.jinx +3 -3
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/setup.jinx +6 -6
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sibiji.npc +3 -3
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/spool.jinx +3 -3
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/team.jinx +12 -12
- npcsh-1.1.22.data/data/npcsh/npc_team/vixynt.jinx +388 -0
- npcsh-1.1.22.data/data/npcsh/npc_team/wander.jinx +728 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/web_search.jinx +52 -14
- npcsh-1.1.22.data/data/npcsh/npc_team/yap.jinx +716 -0
- {npcsh-1.1.20.dist-info → npcsh-1.1.22.dist-info}/METADATA +246 -281
- npcsh-1.1.22.dist-info/RECORD +240 -0
- npcsh-1.1.22.dist-info/entry_points.txt +11 -0
- npcsh/npc_team/jinxs/bin/config_tui.jinx +0 -300
- npcsh/npc_team/jinxs/bin/memories.jinx +0 -317
- npcsh/npc_team/jinxs/bin/vixynt.jinx +0 -122
- npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +0 -418
- npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +0 -73
- npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +0 -388
- npcsh/npc_team/jinxs/lib/core/search.jinx +0 -54
- npcsh/npc_team/jinxs/lib/research/paper_search.jinx +0 -412
- npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +0 -386
- npcsh/npc_team/jinxs/lib/utils/build.jinx +0 -65
- npcsh/npc_team/plonkjr.npc +0 -23
- npcsh-1.1.20.data/data/npcsh/npc_team/alicanto.jinx +0 -356
- npcsh-1.1.20.data/data/npcsh/npc_team/build.jinx +0 -65
- npcsh-1.1.20.data/data/npcsh/npc_team/compress.jinx +0 -140
- npcsh-1.1.20.data/data/npcsh/npc_team/config_tui.jinx +0 -300
- npcsh-1.1.20.data/data/npcsh/npc_team/corca.jinx +0 -430
- npcsh-1.1.20.data/data/npcsh/npc_team/edit_file.jinx +0 -97
- npcsh-1.1.20.data/data/npcsh/npc_team/kg_search.jinx +0 -418
- npcsh-1.1.20.data/data/npcsh/npc_team/mem_review.jinx +0 -73
- npcsh-1.1.20.data/data/npcsh/npc_team/mem_search.jinx +0 -388
- npcsh-1.1.20.data/data/npcsh/npc_team/memories.jinx +0 -317
- npcsh-1.1.20.data/data/npcsh/npc_team/paper_search.jinx +0 -412
- npcsh-1.1.20.data/data/npcsh/npc_team/plonk.jinx +0 -379
- npcsh-1.1.20.data/data/npcsh/npc_team/plonkjr.npc +0 -23
- npcsh-1.1.20.data/data/npcsh/npc_team/search.jinx +0 -54
- npcsh-1.1.20.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -386
- npcsh-1.1.20.data/data/npcsh/npc_team/vixynt.jinx +0 -122
- npcsh-1.1.20.data/data/npcsh/npc_team/wander.jinx +0 -455
- npcsh-1.1.20.data/data/npcsh/npc_team/yap.jinx +0 -268
- npcsh-1.1.20.dist-info/RECORD +0 -248
- npcsh-1.1.20.dist-info/entry_points.txt +0 -25
- /npcsh/npc_team/jinxs/lib/{orchestration → core}/convene.jinx +0 -0
- /npcsh/npc_team/jinxs/lib/{orchestration → core}/delegate.jinx +0 -0
- /npcsh/npc_team/jinxs/{bin → lib/core}/sample.jinx +0 -0
- /npcsh/npc_team/jinxs/lib/{core → utils}/chat.jinx +0 -0
- /npcsh/npc_team/jinxs/lib/{core → utils}/cmd.jinx +0 -0
- /npcsh/npc_team/jinxs/{bin → lib/utils}/sync.jinx +0 -0
- /npcsh/npc_team/jinxs/{bin → modes}/roll.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/add_tab.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/chat.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/click.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_pane.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_tab.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/cmd.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/compile.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/confirm.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/convene.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca_example.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/delegate.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/focus_pane.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.npc +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/help.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/incognide.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/init.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/key_press.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/list_panes.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/load_file.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/navigate.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/notify.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/open_pane.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/ots.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/paste.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonk.npc +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/pti.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/python.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/read_pane.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/roll.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/run_terminal.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sample.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/send_message.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/serve.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/set.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sh.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/shh.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sleep.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/split_pane.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sql.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch_npc.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch_tab.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switches.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sync.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/trigger.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/type_text.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/usage.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/verbose.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/wait.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/write_file.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/zen_mode.jinx +0 -0
- {npcsh-1.1.20.dist-info → npcsh-1.1.22.dist-info}/WHEEL +0 -0
- {npcsh-1.1.20.dist-info → npcsh-1.1.22.dist-info}/licenses/LICENSE +0 -0
- {npcsh-1.1.20.dist-info → npcsh-1.1.22.dist-info}/top_level.txt +0 -0
|
@@ -1,455 +0,0 @@
|
|
|
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[7m {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
|
-
}
|