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
|
@@ -11,13 +11,19 @@ colors:
|
|
|
11
11
|
top: "255,215,0"
|
|
12
12
|
bottom: "218,165,32"
|
|
13
13
|
primary_directive: |
|
|
14
|
-
You are alicanto,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
You are alicanto, a research agent. You investigate hypotheses through experimentation and evidence gathering.
|
|
15
|
+
Search academic papers to ground your work in existing literature.
|
|
16
|
+
Search the web for data, documentation, and recent findings.
|
|
17
|
+
Write and execute Python code to analyze data, compute statistics, and generate results.
|
|
18
|
+
Use shell commands for data processing and system tasks.
|
|
19
|
+
Create files to record your findings, analyses, and evidence.
|
|
20
|
+
When exploring a hypothesis, gather evidence from multiple sources, analyze it quantitatively where possible, and document what you find.
|
|
21
|
+
Say RESEARCH_COMPLETE when you have sufficient evidence to evaluate your hypothesis.
|
|
18
22
|
jinxs:
|
|
19
|
-
- lib/core/search
|
|
23
|
+
- lib/core/search/web_search
|
|
24
|
+
- lib/core/search/file_search
|
|
25
|
+
- lib/core/search/db_search
|
|
20
26
|
- lib/core/sh
|
|
21
27
|
- lib/core/python
|
|
22
28
|
- lib/core/load_file
|
|
23
|
-
- lib/
|
|
29
|
+
- lib/core/edit_file
|
|
@@ -417,7 +417,7 @@ steps:
|
|
|
417
417
|
header = " ARXIV: '" + query + "' (" + str(len(papers)) + " papers) [sort:" + sort_key + "] "
|
|
418
418
|
else:
|
|
419
419
|
header = " " + papers[selected]['title'][:width-4] + " "
|
|
420
|
-
sys.stdout.write('\033[
|
|
420
|
+
sys.stdout.write('\033[7;1m' + header.ljust(width) + '\033[0m\n')
|
|
421
421
|
|
|
422
422
|
if mode == 'list':
|
|
423
423
|
# Column headers
|
|
@@ -456,7 +456,7 @@ steps:
|
|
|
456
456
|
sys.stdout.write(' ' + line)
|
|
457
457
|
|
|
458
458
|
sys.stdout.write('\033[' + str(height-1) + ';1H\033[K\033[90m' + ("─" * width) + '\033[0m')
|
|
459
|
-
sys.stdout.write('\033[' + str(height) + ';1H\033[K\033[
|
|
459
|
+
sys.stdout.write('\033[' + str(height) + ';1H\033[K\033[7m j/k:Nav Enter:View o/i/p:Open d:Download 1-3:Sort y/a:Filter c:Clear q:Quit [' + str(selected+1) + '/' + str(len(papers)) + '] \033[0m')
|
|
460
460
|
|
|
461
461
|
elif mode == 'detail':
|
|
462
462
|
sys.stdout.write('\033[90m' + ("─" * width) + '\033[0m\n')
|
|
@@ -489,13 +489,13 @@ steps:
|
|
|
489
489
|
sys.stdout.write(' ' + lines[idx][:width-4])
|
|
490
490
|
|
|
491
491
|
sys.stdout.write('\033[' + str(height-1) + ';1H\033[K\033[90m' + ("─" * width) + '\033[0m')
|
|
492
|
-
sys.stdout.write('\033[' + str(height) + ';1H\033[K\033[
|
|
492
|
+
sys.stdout.write('\033[' + str(height) + ';1H\033[K\033[7m j/k:Scroll b:Back o:Browser i:Incognide p:PDF d:Download v:TermView q:Quit \033[0m')
|
|
493
493
|
|
|
494
494
|
elif mode == 'pdfview':
|
|
495
495
|
# PDF terminal view mode
|
|
496
496
|
p = papers[selected]
|
|
497
497
|
header = " PDF VIEW: " + p['aid'] + " "
|
|
498
|
-
sys.stdout.write('\033[
|
|
498
|
+
sys.stdout.write('\033[7;1m' + header.ljust(width) + '\033[0m\n')
|
|
499
499
|
sys.stdout.write('\033[90m' + ("─" * width) + '\033[0m\n')
|
|
500
500
|
|
|
501
501
|
view_height = height - 4
|
|
@@ -510,7 +510,7 @@ steps:
|
|
|
510
510
|
|
|
511
511
|
sys.stdout.write('\033[' + str(height-1) + ';1H\033[K\033[90m' + ("─" * width) + '\033[0m')
|
|
512
512
|
pct = int((pdf_scroll / max(1, len(pdf_lines) - view_height)) * 100) if len(pdf_lines) > view_height else 100
|
|
513
|
-
sys.stdout.write('\033[' + str(height) + ';1H\033[K\033[
|
|
513
|
+
sys.stdout.write('\033[' + str(height) + ';1H\033[K\033[7m j/k/PgDn/PgUp:Scroll b:Back d:Download q:Quit [' + str(pct) + '%] \033[0m')
|
|
514
514
|
|
|
515
515
|
sys.stdout.flush()
|
|
516
516
|
|
|
@@ -23,7 +23,7 @@ steps:
|
|
|
23
23
|
if not model:
|
|
24
24
|
model = npc.model if npc and npc.model else ""
|
|
25
25
|
if not provider:
|
|
26
|
-
provider = npc.provider if npc and npc.provider else "
|
|
26
|
+
provider = npc.provider if npc and npc.provider else ""
|
|
27
27
|
|
|
28
28
|
try:
|
|
29
29
|
from npcsh.benchmark import BenchmarkRunner, run_benchmark
|
|
@@ -56,7 +56,7 @@ steps:
|
|
|
56
56
|
output += "\n### Usage:\n"
|
|
57
57
|
output += "```\n"
|
|
58
58
|
output += "/benchmark action=quick\n"
|
|
59
|
-
output += "/benchmark action=run model
|
|
59
|
+
output += "/benchmark action=run model=<model> provider=<provider>\n"
|
|
60
60
|
output += "/benchmark action=list\n"
|
|
61
61
|
output += "```\n"
|
|
62
62
|
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
jinx_name: build
|
|
2
|
+
description: Interactive TUI for building deployment artifacts from an NPC team
|
|
3
|
+
interactive: true
|
|
4
|
+
inputs:
|
|
5
|
+
- target: ""
|
|
6
|
+
- outdir: "./build"
|
|
7
|
+
- team: "./npc_team"
|
|
8
|
+
- port: 5337
|
|
9
|
+
- cors: ""
|
|
10
|
+
steps:
|
|
11
|
+
- name: build
|
|
12
|
+
engine: python
|
|
13
|
+
code: |
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
import tty
|
|
17
|
+
import termios
|
|
18
|
+
import select as _sel
|
|
19
|
+
|
|
20
|
+
def _resolve_team_path(raw_team):
|
|
21
|
+
"""Resolve team path: try given path first, then local ./npc_team, then global ~/.npcsh/npc_team."""
|
|
22
|
+
candidates = []
|
|
23
|
+
if raw_team:
|
|
24
|
+
candidates.append(os.path.abspath(os.path.expanduser(raw_team)))
|
|
25
|
+
local = os.path.abspath('./npc_team')
|
|
26
|
+
global_ = os.path.expanduser('~/.npcsh/npc_team')
|
|
27
|
+
if local not in candidates:
|
|
28
|
+
candidates.append(local)
|
|
29
|
+
if global_ not in candidates:
|
|
30
|
+
candidates.append(global_)
|
|
31
|
+
for p in candidates:
|
|
32
|
+
if os.path.isdir(p):
|
|
33
|
+
if p != candidates[0] and candidates[0] != p:
|
|
34
|
+
print(f"\033[33m⚠ Local team not found at {candidates[0]}, using {p}\033[0m")
|
|
35
|
+
return p
|
|
36
|
+
raise FileNotFoundError(
|
|
37
|
+
f"No npc_team directory found. Searched:\n"
|
|
38
|
+
+ "\n".join(f" - {c}" for c in candidates)
|
|
39
|
+
+ "\n\nCreate a local npc_team/ or ensure ~/.npcsh/npc_team exists."
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
_direct_target = (context.get('target') or '').strip().lower()
|
|
43
|
+
_direct = bool(_direct_target) or not sys.stdin.isatty()
|
|
44
|
+
|
|
45
|
+
if _direct:
|
|
46
|
+
# Direct build: target passed explicitly or non-interactive
|
|
47
|
+
try:
|
|
48
|
+
from npcpy.build_funcs import (
|
|
49
|
+
build_flask_server as _bf,
|
|
50
|
+
build_docker_compose as _bd,
|
|
51
|
+
build_cli_executable as _bc,
|
|
52
|
+
build_static_site as _bs,
|
|
53
|
+
)
|
|
54
|
+
_target = _direct_target or 'flask'
|
|
55
|
+
_builders = {'flask': _bf, 'docker': _bd, 'cli': _bc, 'static': _bs}
|
|
56
|
+
if _target not in _builders:
|
|
57
|
+
context['output'] = f"Unknown target: {_target}. Available: {list(_builders.keys())}"
|
|
58
|
+
else:
|
|
59
|
+
_cfg = {
|
|
60
|
+
'team_path': _resolve_team_path(context.get('team')),
|
|
61
|
+
'output_dir': os.path.abspath(os.path.expanduser(context.get('outdir') or './build')),
|
|
62
|
+
'target': _target,
|
|
63
|
+
'port': int(context.get('port') or 5337),
|
|
64
|
+
'cors_origins': [c.strip() for c in (context.get('cors') or '').split(',') if c.strip()] or None,
|
|
65
|
+
}
|
|
66
|
+
_r = _builders[_target](_cfg)
|
|
67
|
+
context['output'] = _r.get('output', 'Build complete.')
|
|
68
|
+
except ImportError:
|
|
69
|
+
context['output'] = "Build functions not available. Install npcpy with build support."
|
|
70
|
+
except Exception as _e:
|
|
71
|
+
context['output'] = f"Build failed: {_e}"
|
|
72
|
+
context['messages'] = context.get('messages', [])
|
|
73
|
+
exit()
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
from npcpy.build_funcs import (
|
|
77
|
+
build_flask_server,
|
|
78
|
+
build_docker_compose,
|
|
79
|
+
build_cli_executable,
|
|
80
|
+
build_static_site,
|
|
81
|
+
)
|
|
82
|
+
BUILD_AVAILABLE = True
|
|
83
|
+
except ImportError:
|
|
84
|
+
BUILD_AVAILABLE = False
|
|
85
|
+
|
|
86
|
+
if not BUILD_AVAILABLE:
|
|
87
|
+
context['output'] = "Build functions not available. Install npcpy with build support."
|
|
88
|
+
exit()
|
|
89
|
+
|
|
90
|
+
# ========== State ==========
|
|
91
|
+
class BuildState:
|
|
92
|
+
def __init__(self):
|
|
93
|
+
self.phase = 0 # 0=select target, 1=configure, 2=building, 3=result
|
|
94
|
+
self.sel = 0
|
|
95
|
+
self.targets = [
|
|
96
|
+
{'key': 'flask', 'name': 'Flask Server', 'desc': 'Standalone Python web server with NPC API endpoints'},
|
|
97
|
+
{'key': 'docker', 'name': 'Docker', 'desc': 'Containerized deployment with Dockerfile and docker-compose'},
|
|
98
|
+
{'key': 'cli', 'name': 'CLI Scripts', 'desc': 'Per-NPC executable scripts for direct CLI usage'},
|
|
99
|
+
{'key': 'static', 'name': 'Static Site', 'desc': 'HTML documentation page listing team NPCs'},
|
|
100
|
+
]
|
|
101
|
+
try:
|
|
102
|
+
_resolved_team = _resolve_team_path(context.get('team'))
|
|
103
|
+
except FileNotFoundError:
|
|
104
|
+
_resolved_team = os.path.expanduser(context.get('team') or './npc_team')
|
|
105
|
+
self.config = {
|
|
106
|
+
'outdir': os.path.expanduser(context.get('outdir') or './build'),
|
|
107
|
+
'team': _resolved_team,
|
|
108
|
+
'port': str(context.get('port') or 5337),
|
|
109
|
+
'cors': context.get('cors') or '',
|
|
110
|
+
}
|
|
111
|
+
self.config_keys = ['outdir', 'team', 'port', 'cors']
|
|
112
|
+
self.config_labels = {'outdir': 'Output Dir', 'team': 'Team Path', 'port': 'Port', 'cors': 'CORS Origins'}
|
|
113
|
+
self.config_sel = 0
|
|
114
|
+
self.editing = False
|
|
115
|
+
self.edit_buf = ""
|
|
116
|
+
self.edit_key = ""
|
|
117
|
+
self.result = ""
|
|
118
|
+
self.error = ""
|
|
119
|
+
self.files_created = []
|
|
120
|
+
|
|
121
|
+
ui = BuildState()
|
|
122
|
+
|
|
123
|
+
# TUI mode: no target was passed, user picks interactively
|
|
124
|
+
|
|
125
|
+
# ========== Helpers ==========
|
|
126
|
+
def get_size():
|
|
127
|
+
try:
|
|
128
|
+
s = os.get_terminal_size()
|
|
129
|
+
return s.columns, s.lines
|
|
130
|
+
except:
|
|
131
|
+
return 80, 24
|
|
132
|
+
|
|
133
|
+
# ========== Rendering ==========
|
|
134
|
+
def render():
|
|
135
|
+
width, height = get_size()
|
|
136
|
+
out = []
|
|
137
|
+
out.append('\033[2J\033[H')
|
|
138
|
+
|
|
139
|
+
phase_names = ['Select Target', 'Configure', 'Building...', 'Complete']
|
|
140
|
+
header = f' NPC BUILD - {phase_names[ui.phase]} '
|
|
141
|
+
out.append(f'\033[1;1H\033[7;1m{header.ljust(width)}\033[0m')
|
|
142
|
+
|
|
143
|
+
if ui.phase == 0:
|
|
144
|
+
render_targets(out, width, height)
|
|
145
|
+
elif ui.phase == 1:
|
|
146
|
+
render_config(out, width, height)
|
|
147
|
+
elif ui.phase == 2:
|
|
148
|
+
render_building(out, width, height)
|
|
149
|
+
elif ui.phase == 3:
|
|
150
|
+
render_result(out, width, height)
|
|
151
|
+
|
|
152
|
+
if ui.error:
|
|
153
|
+
out.append(f'\033[{height-1};1H\033[K \033[31m{ui.error[:width-3]}\033[0m')
|
|
154
|
+
|
|
155
|
+
sys.stdout.write(''.join(out))
|
|
156
|
+
sys.stdout.flush()
|
|
157
|
+
|
|
158
|
+
def render_targets(out, width, height):
|
|
159
|
+
banner = [
|
|
160
|
+
'\033[33m ██████╗ ██╗ ██╗██╗██╗ ██████╗ \033[0m',
|
|
161
|
+
'\033[33m██╔══██╗██║ ██║██║██║ ██╔══██╗\033[0m',
|
|
162
|
+
'\033[33m██████╔╝██║ ██║██║██║ ██║ ██║\033[0m',
|
|
163
|
+
'\033[33m██╔══██╗██║ ██║██║██║ ██║ ██║\033[0m',
|
|
164
|
+
'\033[33m██████╔╝╚██████╔╝██║███████╗██████╔╝\033[0m',
|
|
165
|
+
'\033[33m╚═════╝ ╚═════╝ ╚═╝╚══════╝╚═════╝ \033[0m',
|
|
166
|
+
]
|
|
167
|
+
for i, line in enumerate(banner):
|
|
168
|
+
out.append(f'\033[{3+i};3H{line}')
|
|
169
|
+
|
|
170
|
+
y = 3 + len(banner) + 1
|
|
171
|
+
out.append(f'\033[{y};3H\033[1mSelect a build target:\033[0m')
|
|
172
|
+
y += 2
|
|
173
|
+
|
|
174
|
+
for i, target in enumerate(ui.targets):
|
|
175
|
+
selected = (i == ui.sel)
|
|
176
|
+
if selected:
|
|
177
|
+
out.append(f'\033[{y};2H\033[7;1m > {target["name"]:<20}\033[0m \033[7m{target["desc"][:width-28]}\033[0m')
|
|
178
|
+
else:
|
|
179
|
+
out.append(f'\033[{y};2H \033[1m{target["name"]:<20}\033[0m \033[90m{target["desc"][:width-28]}\033[0m')
|
|
180
|
+
y += 2
|
|
181
|
+
|
|
182
|
+
out.append(f'\033[{height};1H\033[K\033[7m j/k:Navigate Enter:Select q:Quit \033[0m'.ljust(width))
|
|
183
|
+
|
|
184
|
+
def render_config(out, width, height):
|
|
185
|
+
target = ui.targets[ui.sel]
|
|
186
|
+
out.append(f'\033[3;3H\033[1mTarget: \033[36m{target["name"]}\033[0m')
|
|
187
|
+
out.append(f'\033[4;3H\033[90m{target["desc"]}\033[0m')
|
|
188
|
+
|
|
189
|
+
out.append(f'\033[6;3H\033[1mConfiguration:\033[0m')
|
|
190
|
+
|
|
191
|
+
y = 8
|
|
192
|
+
for i, key in enumerate(ui.config_keys):
|
|
193
|
+
label = ui.config_labels[key]
|
|
194
|
+
val = ui.config[key]
|
|
195
|
+
selected = (i == ui.config_sel)
|
|
196
|
+
|
|
197
|
+
if ui.editing and key == ui.edit_key:
|
|
198
|
+
out.append(f'\033[{y};3H\033[33m{label}:\033[0m \033[7m {ui.edit_buf}_ \033[0m')
|
|
199
|
+
elif selected:
|
|
200
|
+
out.append(f'\033[{y};3H\033[7m {label}: {val} \033[0m')
|
|
201
|
+
else:
|
|
202
|
+
out.append(f'\033[{y};3H \033[1m{label}:\033[0m {val}')
|
|
203
|
+
y += 2
|
|
204
|
+
|
|
205
|
+
# Show which configs are relevant for this target
|
|
206
|
+
y += 1
|
|
207
|
+
relevant = {'flask': ['outdir', 'team', 'port', 'cors'],
|
|
208
|
+
'docker': ['outdir', 'team', 'port', 'cors'],
|
|
209
|
+
'cli': ['outdir', 'team'],
|
|
210
|
+
'static': ['outdir', 'team']}
|
|
211
|
+
rel = relevant.get(target['key'], ui.config_keys)
|
|
212
|
+
out.append(f'\033[{y};3H\033[90mRelevant for {target["name"]}: {", ".join(rel)}\033[0m')
|
|
213
|
+
|
|
214
|
+
if ui.editing:
|
|
215
|
+
out.append(f'\033[{height};1H\033[K\033[7m Enter:Save Esc:Cancel \033[0m'.ljust(width))
|
|
216
|
+
else:
|
|
217
|
+
out.append(f'\033[{height};1H\033[K\033[7m j/k:Navigate e:Edit Enter:Build Backspace:Back q:Quit \033[0m'.ljust(width))
|
|
218
|
+
|
|
219
|
+
def render_building(out, width, height):
|
|
220
|
+
target = ui.targets[ui.sel]
|
|
221
|
+
mid = height // 2
|
|
222
|
+
out.append(f'\033[{mid};{width//2-10}H\033[33;1mBuilding {target["name"]}...\033[0m')
|
|
223
|
+
|
|
224
|
+
def render_result(out, width, height):
|
|
225
|
+
target = ui.targets[ui.sel]
|
|
226
|
+
y = 3
|
|
227
|
+
if ui.error:
|
|
228
|
+
out.append(f'\033[{y};3H\033[31;1mBuild Failed\033[0m')
|
|
229
|
+
y += 2
|
|
230
|
+
out.append(f'\033[{y};3H\033[31m{ui.error[:width-6]}\033[0m')
|
|
231
|
+
else:
|
|
232
|
+
out.append(f'\033[{y};3H\033[32;1mBuild Complete: {target["name"]}\033[0m')
|
|
233
|
+
y += 2
|
|
234
|
+
for line in ui.result.split('\n'):
|
|
235
|
+
if y >= height - 3:
|
|
236
|
+
break
|
|
237
|
+
out.append(f'\033[{y};3H{line[:width-6]}')
|
|
238
|
+
y += 1
|
|
239
|
+
|
|
240
|
+
out.append(f'\033[{height};1H\033[K\033[7m Enter:New Build o:Open output dir q:Quit \033[0m'.ljust(width))
|
|
241
|
+
|
|
242
|
+
# ========== Build Execution ==========
|
|
243
|
+
def do_build():
|
|
244
|
+
target = ui.targets[ui.sel]
|
|
245
|
+
config = {
|
|
246
|
+
'team_path': _resolve_team_path(ui.config['team']),
|
|
247
|
+
'output_dir': os.path.abspath(os.path.expanduser(ui.config['outdir'])),
|
|
248
|
+
'target': target['key'],
|
|
249
|
+
'port': int(ui.config['port']),
|
|
250
|
+
'cors_origins': [c.strip() for c in ui.config['cors'].split(',') if c.strip()] or None,
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
builders = {
|
|
254
|
+
'flask': build_flask_server,
|
|
255
|
+
'docker': build_docker_compose,
|
|
256
|
+
'cli': build_cli_executable,
|
|
257
|
+
'static': build_static_site,
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
try:
|
|
261
|
+
result = builders[target['key']](config)
|
|
262
|
+
ui.result = result.get('output', 'Build complete.')
|
|
263
|
+
ui.error = ""
|
|
264
|
+
except Exception as e:
|
|
265
|
+
ui.error = str(e)
|
|
266
|
+
ui.result = ""
|
|
267
|
+
|
|
268
|
+
ui.phase = 3
|
|
269
|
+
|
|
270
|
+
# ========== Input ==========
|
|
271
|
+
def handle_input(c, fd):
|
|
272
|
+
if ui.editing:
|
|
273
|
+
return handle_edit(c, fd)
|
|
274
|
+
|
|
275
|
+
if c == '\x1b':
|
|
276
|
+
if _sel.select([fd], [], [], 0.05)[0]:
|
|
277
|
+
c2 = os.read(fd, 1).decode('latin-1')
|
|
278
|
+
if c2 == '[':
|
|
279
|
+
c3 = os.read(fd, 1).decode('latin-1')
|
|
280
|
+
if c3 == 'A': move_up()
|
|
281
|
+
elif c3 == 'B': move_down()
|
|
282
|
+
return True
|
|
283
|
+
|
|
284
|
+
if c == 'q' or c == '\x03':
|
|
285
|
+
return False
|
|
286
|
+
|
|
287
|
+
if ui.phase == 0:
|
|
288
|
+
if c == 'j': move_down()
|
|
289
|
+
elif c == 'k': move_up()
|
|
290
|
+
elif c in ('\r', '\n'):
|
|
291
|
+
ui.phase = 1
|
|
292
|
+
ui.config_sel = 0
|
|
293
|
+
elif ui.phase == 1:
|
|
294
|
+
if c == 'j': move_down()
|
|
295
|
+
elif c == 'k': move_up()
|
|
296
|
+
elif c == 'e':
|
|
297
|
+
key = ui.config_keys[ui.config_sel]
|
|
298
|
+
ui.editing = True
|
|
299
|
+
ui.edit_key = key
|
|
300
|
+
ui.edit_buf = ui.config[key]
|
|
301
|
+
elif c == '\x7f' or c == '\x08':
|
|
302
|
+
ui.phase = 0
|
|
303
|
+
ui.config_sel = 0
|
|
304
|
+
elif c in ('\r', '\n'):
|
|
305
|
+
ui.phase = 2
|
|
306
|
+
render()
|
|
307
|
+
do_build()
|
|
308
|
+
elif ui.phase == 3:
|
|
309
|
+
if c in ('\r', '\n'):
|
|
310
|
+
ui.phase = 0
|
|
311
|
+
ui.sel = 0
|
|
312
|
+
ui.error = ""
|
|
313
|
+
ui.result = ""
|
|
314
|
+
elif c == 'o':
|
|
315
|
+
outdir = os.path.abspath(os.path.expanduser(ui.config['outdir']))
|
|
316
|
+
if os.path.isdir(outdir):
|
|
317
|
+
import subprocess
|
|
318
|
+
try:
|
|
319
|
+
subprocess.Popen(['xdg-open', outdir], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
320
|
+
except:
|
|
321
|
+
pass
|
|
322
|
+
return True
|
|
323
|
+
|
|
324
|
+
def handle_edit(c, fd):
|
|
325
|
+
if c == '\x1b':
|
|
326
|
+
if _sel.select([fd], [], [], 0.05)[0]:
|
|
327
|
+
os.read(fd, 2)
|
|
328
|
+
ui.editing = False
|
|
329
|
+
ui.edit_buf = ""
|
|
330
|
+
return True
|
|
331
|
+
if c in ('\r', '\n'):
|
|
332
|
+
ui.config[ui.edit_key] = ui.edit_buf
|
|
333
|
+
ui.editing = False
|
|
334
|
+
return True
|
|
335
|
+
if c == '\x7f' or c == '\x08':
|
|
336
|
+
ui.edit_buf = ui.edit_buf[:-1]
|
|
337
|
+
return True
|
|
338
|
+
if c >= ' ' and c <= '~':
|
|
339
|
+
ui.edit_buf += c
|
|
340
|
+
return True
|
|
341
|
+
|
|
342
|
+
def move_up():
|
|
343
|
+
if ui.phase == 0:
|
|
344
|
+
ui.sel = max(0, ui.sel - 1)
|
|
345
|
+
elif ui.phase == 1:
|
|
346
|
+
ui.config_sel = max(0, ui.config_sel - 1)
|
|
347
|
+
|
|
348
|
+
def move_down():
|
|
349
|
+
if ui.phase == 0:
|
|
350
|
+
ui.sel = min(len(ui.targets) - 1, ui.sel + 1)
|
|
351
|
+
elif ui.phase == 1:
|
|
352
|
+
ui.config_sel = min(len(ui.config_keys) - 1, ui.config_sel + 1)
|
|
353
|
+
|
|
354
|
+
# ========== Main Loop ==========
|
|
355
|
+
fd = sys.stdin.fileno()
|
|
356
|
+
old_settings = termios.tcgetattr(fd)
|
|
357
|
+
|
|
358
|
+
try:
|
|
359
|
+
tty.setcbreak(fd)
|
|
360
|
+
sys.stdout.write('\033[?25l')
|
|
361
|
+
render()
|
|
362
|
+
|
|
363
|
+
running = True
|
|
364
|
+
while running:
|
|
365
|
+
if _sel.select([fd], [], [], 0.5)[0]:
|
|
366
|
+
c = os.read(fd, 1).decode('latin-1')
|
|
367
|
+
running = handle_input(c, fd)
|
|
368
|
+
render()
|
|
369
|
+
finally:
|
|
370
|
+
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
|
371
|
+
sys.stdout.write('\033[?25h\033[2J\033[H')
|
|
372
|
+
sys.stdout.flush()
|
|
373
|
+
|
|
374
|
+
if ui.result:
|
|
375
|
+
print(ui.result)
|
|
376
|
+
|
|
377
|
+
context['output'] = ui.result or 'Build cancelled.'
|
|
378
|
+
context['messages'] = context.get('messages', [])
|