npcsh 1.1.21__py3-none-any.whl → 1.1.23__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- npcsh/_state.py +282 -125
- npcsh/benchmark/npcsh_agent.py +77 -232
- npcsh/benchmark/templates/install-npcsh.sh.j2 +12 -4
- npcsh/config.py +5 -2
- npcsh/mcp_server.py +9 -1
- npcsh/npc_team/alicanto.npc +8 -6
- npcsh/npc_team/corca.npc +5 -12
- npcsh/npc_team/frederic.npc +6 -9
- npcsh/npc_team/guac.npc +4 -4
- npcsh/npc_team/jinxs/lib/core/delegate.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/edit_file.jinx +84 -62
- npcsh/npc_team/jinxs/lib/core/sh.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/skill.jinx +59 -0
- npcsh/npc_team/jinxs/lib/utils/help.jinx +194 -10
- npcsh/npc_team/jinxs/lib/utils/init.jinx +528 -37
- npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +0 -1
- npcsh/npc_team/jinxs/lib/utils/serve.jinx +938 -21
- npcsh/npc_team/jinxs/modes/alicanto.jinx +102 -41
- npcsh/npc_team/jinxs/modes/build.jinx +378 -0
- npcsh-1.1.21.data/data/npcsh/npc_team/config_tui.jinx → npcsh/npc_team/jinxs/modes/config.jinx +1 -1
- npcsh/npc_team/jinxs/modes/convene.jinx +670 -0
- npcsh/npc_team/jinxs/modes/corca.jinx +777 -387
- npcsh/npc_team/jinxs/modes/crond.jinx +818 -0
- npcsh/npc_team/jinxs/modes/kg.jinx +69 -2
- npcsh/npc_team/jinxs/modes/plonk.jinx +86 -15
- npcsh/npc_team/jinxs/modes/roll.jinx +368 -55
- npcsh/npc_team/jinxs/modes/skills.jinx +621 -0
- npcsh/npc_team/jinxs/modes/yap.jinx +1092 -177
- npcsh/npc_team/jinxs/skills/code-review/SKILL.md +45 -0
- npcsh/npc_team/jinxs/skills/debugging/SKILL.md +44 -0
- npcsh/npc_team/jinxs/skills/git-workflow.jinx +44 -0
- npcsh/npc_team/kadiefa.npc +6 -6
- npcsh/npc_team/npcsh.ctx +16 -0
- npcsh/npc_team/plonk.npc +5 -9
- npcsh/npc_team/sibiji.npc +15 -7
- npcsh/npcsh.py +1 -0
- npcsh/routes.py +0 -4
- npcsh/yap.py +22 -4
- npcsh-1.1.23.data/data/npcsh/npc_team/SKILL.md +44 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.jinx +102 -41
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.npc +8 -6
- npcsh-1.1.23.data/data/npcsh/npc_team/build.jinx +378 -0
- npcsh/npc_team/jinxs/modes/config_tui.jinx → npcsh-1.1.23.data/data/npcsh/npc_team/config.jinx +1 -1
- npcsh-1.1.23.data/data/npcsh/npc_team/convene.jinx +670 -0
- npcsh-1.1.23.data/data/npcsh/npc_team/corca.jinx +820 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca.npc +5 -12
- npcsh-1.1.23.data/data/npcsh/npc_team/crond.jinx +818 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/delegate.jinx +1 -1
- npcsh-1.1.23.data/data/npcsh/npc_team/edit_file.jinx +119 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/frederic.npc +6 -9
- npcsh-1.1.23.data/data/npcsh/npc_team/git-workflow.jinx +44 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.npc +4 -4
- npcsh-1.1.23.data/data/npcsh/npc_team/help.jinx +236 -0
- npcsh-1.1.23.data/data/npcsh/npc_team/init.jinx +532 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/jinxs.jinx +0 -1
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kadiefa.npc +6 -6
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kg.jinx +69 -2
- npcsh-1.1.23.data/data/npcsh/npc_team/npcsh.ctx +34 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.jinx +86 -15
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.npc +5 -9
- npcsh-1.1.23.data/data/npcsh/npc_team/roll.jinx +378 -0
- npcsh-1.1.23.data/data/npcsh/npc_team/serve.jinx +943 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sh.jinx +1 -1
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sibiji.npc +15 -7
- npcsh-1.1.23.data/data/npcsh/npc_team/skill.jinx +59 -0
- npcsh-1.1.23.data/data/npcsh/npc_team/skills.jinx +621 -0
- npcsh-1.1.23.data/data/npcsh/npc_team/yap.jinx +1190 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/METADATA +404 -278
- npcsh-1.1.23.dist-info/RECORD +216 -0
- npcsh/npc_team/jinxs/incognide/add_tab.jinx +0 -11
- npcsh/npc_team/jinxs/incognide/close_pane.jinx +0 -9
- npcsh/npc_team/jinxs/incognide/close_tab.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/confirm.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/focus_pane.jinx +0 -9
- npcsh/npc_team/jinxs/incognide/list_panes.jinx +0 -8
- npcsh/npc_team/jinxs/incognide/navigate.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/notify.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/open_pane.jinx +0 -13
- npcsh/npc_team/jinxs/incognide/read_pane.jinx +0 -9
- npcsh/npc_team/jinxs/incognide/run_terminal.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/send_message.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/split_pane.jinx +0 -12
- npcsh/npc_team/jinxs/incognide/switch_npc.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/switch_tab.jinx +0 -10
- npcsh/npc_team/jinxs/incognide/write_file.jinx +0 -11
- npcsh/npc_team/jinxs/incognide/zen_mode.jinx +0 -9
- npcsh/npc_team/jinxs/lib/core/convene.jinx +0 -232
- npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +0 -429
- npcsh/npc_team/jinxs/lib/core/search.jinx +0 -54
- npcsh/npc_team/jinxs/lib/utils/build.jinx +0 -65
- npcsh-1.1.21.data/data/npcsh/npc_team/add_tab.jinx +0 -11
- npcsh-1.1.21.data/data/npcsh/npc_team/build.jinx +0 -65
- npcsh-1.1.21.data/data/npcsh/npc_team/close_pane.jinx +0 -9
- npcsh-1.1.21.data/data/npcsh/npc_team/close_tab.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/confirm.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/convene.jinx +0 -232
- npcsh-1.1.21.data/data/npcsh/npc_team/corca.jinx +0 -430
- npcsh-1.1.21.data/data/npcsh/npc_team/edit_file.jinx +0 -97
- npcsh-1.1.21.data/data/npcsh/npc_team/focus_pane.jinx +0 -9
- npcsh-1.1.21.data/data/npcsh/npc_team/help.jinx +0 -52
- npcsh-1.1.21.data/data/npcsh/npc_team/init.jinx +0 -41
- npcsh-1.1.21.data/data/npcsh/npc_team/kg_search.jinx +0 -429
- npcsh-1.1.21.data/data/npcsh/npc_team/list_panes.jinx +0 -8
- npcsh-1.1.21.data/data/npcsh/npc_team/navigate.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/notify.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/npcsh.ctx +0 -18
- npcsh-1.1.21.data/data/npcsh/npc_team/open_pane.jinx +0 -13
- npcsh-1.1.21.data/data/npcsh/npc_team/read_pane.jinx +0 -9
- npcsh-1.1.21.data/data/npcsh/npc_team/roll.jinx +0 -65
- npcsh-1.1.21.data/data/npcsh/npc_team/run_terminal.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/search.jinx +0 -54
- npcsh-1.1.21.data/data/npcsh/npc_team/send_message.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/serve.jinx +0 -26
- npcsh-1.1.21.data/data/npcsh/npc_team/split_pane.jinx +0 -12
- npcsh-1.1.21.data/data/npcsh/npc_team/switch_npc.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/switch_tab.jinx +0 -10
- npcsh-1.1.21.data/data/npcsh/npc_team/write_file.jinx +0 -11
- npcsh-1.1.21.data/data/npcsh/npc_team/yap.jinx +0 -275
- npcsh-1.1.21.data/data/npcsh/npc_team/zen_mode.jinx +0 -9
- npcsh-1.1.21.dist-info/RECORD +0 -243
- /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/{incognide → lib/utils}/incognide.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/arxiv.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/benchmark.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/chat.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/click.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/cmd.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/compile.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/compress.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca_example.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/db_search.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/file_search.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/git.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/incognide.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/key_press.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/load_file.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/memories.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/models.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/nql.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/ots.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/papers.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/paste.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/pti.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/python.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/reattach.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sample.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/set.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/setup.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/shh.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sleep.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/spool.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sql.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/switch.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/switches.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sync.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/team.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/trigger.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/type_text.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/usage.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/verbose.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/vixynt.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/wait.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/wander.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/web_search.jinx +0 -0
- {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/WHEEL +0 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/entry_points.txt +0 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/licenses/LICENSE +0 -0
- {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/top_level.txt +0 -0
npcsh/benchmark/npcsh_agent.py
CHANGED
|
@@ -31,6 +31,9 @@ class NpcshAgent(BaseInstalledAgent):
|
|
|
31
31
|
|
|
32
32
|
SUPPORTS_ATIF = True # Agent Trajectory Interchange Format
|
|
33
33
|
|
|
34
|
+
# Root of the npcsh source tree (two levels up from this file)
|
|
35
|
+
_NPCSH_SRC = Path(__file__).resolve().parent.parent.parent
|
|
36
|
+
|
|
34
37
|
def __init__(self, logs_dir: Path = None, model_name: str = None, logger=None, **kwargs):
|
|
35
38
|
super().__init__(logs_dir=logs_dir, model_name=model_name, logger=logger, **kwargs)
|
|
36
39
|
|
|
@@ -43,134 +46,96 @@ class NpcshAgent(BaseInstalledAgent):
|
|
|
43
46
|
"""Path to the jinja template script for installing npcsh in the container."""
|
|
44
47
|
return Path(__file__).parent / "templates" / "install-npcsh.sh.j2"
|
|
45
48
|
|
|
46
|
-
def
|
|
47
|
-
"""
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
""
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
49
|
+
async def setup(self, environment) -> None:
|
|
50
|
+
"""Upload local npcsh + npcpy source, then run install script."""
|
|
51
|
+
import shutil
|
|
52
|
+
import tempfile
|
|
53
|
+
|
|
54
|
+
npcsh_src = self._NPCSH_SRC
|
|
55
|
+
npcpy_src = npcsh_src.parent / "npcpy"
|
|
56
|
+
|
|
57
|
+
# Create /src in container
|
|
58
|
+
await environment.exec(command="mkdir -p /src")
|
|
59
|
+
|
|
60
|
+
# Copy source to temp dir excluding .git and caches
|
|
61
|
+
def _copy_clean(src, name):
|
|
62
|
+
tmp = Path(tempfile.mkdtemp()) / name
|
|
63
|
+
shutil.copytree(
|
|
64
|
+
src, tmp,
|
|
65
|
+
ignore=shutil.ignore_patterns(
|
|
66
|
+
'.git', '__pycache__', '*.pyc', 'dist', 'build',
|
|
67
|
+
'*.egg-info', 'jobs', 'dataset_cache',
|
|
68
|
+
),
|
|
69
|
+
)
|
|
70
|
+
return tmp
|
|
71
|
+
|
|
72
|
+
clean_npcsh = _copy_clean(npcsh_src, "npcsh")
|
|
73
|
+
await environment.upload_dir(
|
|
74
|
+
source_dir=str(clean_npcsh),
|
|
75
|
+
target_dir="/src/npcsh",
|
|
76
|
+
)
|
|
67
77
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
if npcpy_src.exists():
|
|
79
|
+
clean_npcpy = _copy_clean(npcpy_src, "npcpy")
|
|
80
|
+
await environment.upload_dir(
|
|
81
|
+
source_dir=str(clean_npcpy),
|
|
82
|
+
target_dir="/src/npcpy",
|
|
83
|
+
)
|
|
74
84
|
|
|
75
|
-
|
|
85
|
+
await super().setup(environment)
|
|
76
86
|
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
def create_run_agent_commands(self, instruction: str) -> list:
|
|
88
|
+
"""Run instruction through npcsh -c, which handles everything."""
|
|
89
|
+
escaped_instruction = shlex.quote(instruction)
|
|
79
90
|
|
|
80
|
-
|
|
91
|
+
# Forward env vars into the container — npcsh reads these directly
|
|
92
|
+
env_vars = []
|
|
93
|
+
for key in [
|
|
94
|
+
"ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GOOGLE_API_KEY",
|
|
95
|
+
"GEMINI_API_KEY", "DEEPSEEK_API_KEY", "GROQ_API_KEY",
|
|
96
|
+
"OPENROUTER_API_KEY", "OLLAMA_HOST",
|
|
97
|
+
]:
|
|
98
|
+
val = os.environ.get(key)
|
|
99
|
+
if val:
|
|
100
|
+
env_vars.append(f'{key}="{val}"')
|
|
101
|
+
|
|
102
|
+
# Model/provider from Harbor's model_name (e.g. "ollama/phi4")
|
|
103
|
+
model_name = self.model_name or ""
|
|
104
|
+
if "/" in model_name:
|
|
81
105
|
provider, model = model_name.split("/", 1)
|
|
82
|
-
elif model_name:
|
|
83
|
-
provider = os.environ.get("NPCSH_CHAT_PROVIDER", "")
|
|
84
|
-
model = model_name
|
|
85
106
|
else:
|
|
86
107
|
provider = os.environ.get("NPCSH_CHAT_PROVIDER", "")
|
|
87
|
-
model = os.environ.get("NPCSH_CHAT_MODEL", "")
|
|
88
|
-
|
|
89
|
-
# Map provider names to npcsh provider format
|
|
90
|
-
provider_map = {
|
|
91
|
-
"anthropic": "anthropic",
|
|
92
|
-
"openai": "openai",
|
|
93
|
-
"google": "gemini",
|
|
94
|
-
"gemini": "gemini",
|
|
95
|
-
"deepseek": "deepseek",
|
|
96
|
-
"ollama": "ollama",
|
|
97
|
-
"groq": "groq",
|
|
98
|
-
"openrouter": "openrouter",
|
|
99
|
-
}
|
|
100
|
-
npcsh_provider = provider_map.get(provider.lower(), provider)
|
|
101
|
-
|
|
102
|
-
# Build environment variables for API keys
|
|
103
|
-
env_vars = []
|
|
104
|
-
api_key_map = {
|
|
105
|
-
"anthropic": ["ANTHROPIC_API_KEY"],
|
|
106
|
-
"openai": ["OPENAI_API_KEY"],
|
|
107
|
-
"gemini": ["GOOGLE_API_KEY", "GEMINI_API_KEY"],
|
|
108
|
-
"google": ["GOOGLE_API_KEY", "GEMINI_API_KEY"],
|
|
109
|
-
"deepseek": ["DEEPSEEK_API_KEY"],
|
|
110
|
-
"groq": ["GROQ_API_KEY"],
|
|
111
|
-
"openrouter": ["OPENROUTER_API_KEY"],
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
added_keys = set()
|
|
115
|
-
for prov, env_keys in api_key_map.items():
|
|
116
|
-
for env_key in env_keys:
|
|
117
|
-
if env_key in os.environ:
|
|
118
|
-
# For Gemini, always pass as GOOGLE_API_KEY (what litellm expects)
|
|
119
|
-
target_key = "GOOGLE_API_KEY" if env_key == "GEMINI_API_KEY" else env_key
|
|
120
|
-
if target_key not in added_keys:
|
|
121
|
-
env_vars.append(f'{target_key}="{os.environ[env_key]}"')
|
|
122
|
-
added_keys.add(target_key)
|
|
123
|
-
break
|
|
124
|
-
|
|
125
|
-
env_prefix = " ".join(env_vars) + " " if env_vars else ""
|
|
126
|
-
|
|
127
|
-
# Output directory for logs
|
|
128
|
-
output_dir = str(self.logs_dir / "npcsh_output")
|
|
129
|
-
output_file = str(self.logs_dir / "npcsh_output" / "output.jsonl")
|
|
108
|
+
model = model_name or os.environ.get("NPCSH_CHAT_MODEL", "")
|
|
130
109
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
command=f"mkdir -p {shlex.quote(output_dir)}",
|
|
136
|
-
timeout_sec=30
|
|
137
|
-
))
|
|
138
|
-
|
|
139
|
-
# Create .npcsh_global file to use global team and avoid interactive prompts
|
|
140
|
-
commands.append(ExecInput(
|
|
141
|
-
command="touch /app/.npcsh_global",
|
|
142
|
-
timeout_sec=10
|
|
143
|
-
))
|
|
144
|
-
|
|
145
|
-
# Run npcsh with the instruction
|
|
146
|
-
# Using corca NPC which has edit_file tool for writing files
|
|
147
|
-
# Using the npc CLI which supports single-command execution
|
|
148
|
-
# NPCSH_DEFAULT_MODE=agent enables automatic tool execution
|
|
149
|
-
npcsh_cmd = (
|
|
150
|
-
f'{env_prefix}'
|
|
151
|
-
f'NPCSH_CHAT_MODEL="{model}" '
|
|
152
|
-
f'NPCSH_CHAT_PROVIDER="{npcsh_provider}" '
|
|
153
|
-
f'NPCSH_STREAM_OUTPUT=0 '
|
|
154
|
-
f'NPCSH_DEFAULT_MODE=agent '
|
|
155
|
-
f'npc --npc corca {escaped_instruction} '
|
|
156
|
-
f'2>&1 | tee {shlex.quote(output_file)}'
|
|
157
|
-
)
|
|
110
|
+
env_vars.append(f'NPCSH_CHAT_MODEL="{model}"')
|
|
111
|
+
env_vars.append(f'NPCSH_CHAT_PROVIDER="{provider}"')
|
|
112
|
+
env_vars.append('NPCSH_STREAM_OUTPUT=0')
|
|
113
|
+
env_vars.append('NPCSH_DEBUG=1')
|
|
158
114
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
115
|
+
if provider == "ollama":
|
|
116
|
+
if "OLLAMA_HOST" not in os.environ:
|
|
117
|
+
env_vars.append('OLLAMA_HOST="http://host.docker.internal:11434"')
|
|
118
|
+
# Use 16k context for ollama models to avoid losing instructions
|
|
119
|
+
env_vars.append('NPCSH_OLLAMA_NUM_CTX=16384')
|
|
163
120
|
|
|
164
|
-
|
|
121
|
+
env_prefix = " ".join(env_vars)
|
|
122
|
+
output_dir = str(self.logs_dir / "npcsh_output")
|
|
123
|
+
output_file = str(self.logs_dir / "npcsh_output" / "output.jsonl")
|
|
124
|
+
|
|
125
|
+
return [
|
|
126
|
+
ExecInput(command=f"mkdir -p {shlex.quote(output_dir)}", timeout_sec=30),
|
|
127
|
+
ExecInput(command="touch /app/.npcsh_global", timeout_sec=10),
|
|
128
|
+
ExecInput(
|
|
129
|
+
command=f'{env_prefix} npcsh -c {escaped_instruction} 2>&1 | tee {shlex.quote(output_file)}',
|
|
130
|
+
timeout_sec=1800,
|
|
131
|
+
),
|
|
132
|
+
]
|
|
165
133
|
|
|
166
134
|
def populate_context_post_run(self, context: AgentContext) -> None:
|
|
167
135
|
"""
|
|
168
136
|
Populate the context with results of the agent execution.
|
|
169
137
|
|
|
170
138
|
Parses the output file to extract token usage metrics.
|
|
171
|
-
|
|
172
|
-
Args:
|
|
173
|
-
context: The AgentContext to populate with metrics
|
|
174
139
|
"""
|
|
175
140
|
output_file = self.logs_dir / "npcsh_output" / "output.jsonl"
|
|
176
141
|
|
|
@@ -183,145 +148,25 @@ Remember: Use edit_file to write code files. Use sh to run commands. VERIFY your
|
|
|
183
148
|
with open(output_file, 'r') as f:
|
|
184
149
|
content = f.read()
|
|
185
150
|
|
|
186
|
-
# Try to parse as JSONL first
|
|
187
151
|
for line in content.strip().split('\n'):
|
|
188
152
|
if not line.strip():
|
|
189
153
|
continue
|
|
190
154
|
try:
|
|
191
155
|
event = json.loads(line)
|
|
192
|
-
# Extract token usage from events if present
|
|
193
156
|
if isinstance(event, dict):
|
|
194
157
|
usage = event.get('usage', {})
|
|
195
158
|
total_input_tokens += usage.get('input_tokens', 0)
|
|
196
159
|
total_output_tokens += usage.get('output_tokens', 0)
|
|
197
160
|
total_cost_usd += usage.get('cost_usd', 0.0)
|
|
198
161
|
except json.JSONDecodeError:
|
|
199
|
-
# Not JSON, just regular output
|
|
200
162
|
pass
|
|
201
163
|
|
|
202
164
|
except Exception as e:
|
|
203
165
|
self.logger.warning(f"Failed to parse npcsh output: {e}")
|
|
204
166
|
|
|
205
|
-
# Set context metrics
|
|
206
167
|
if hasattr(context, 'input_tokens'):
|
|
207
168
|
context.input_tokens = total_input_tokens
|
|
208
169
|
if hasattr(context, 'output_tokens'):
|
|
209
170
|
context.output_tokens = total_output_tokens
|
|
210
171
|
if hasattr(context, 'cost_usd'):
|
|
211
172
|
context.cost_usd = total_cost_usd
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
class NpcshAgentWithNpc(NpcshAgent):
|
|
215
|
-
"""
|
|
216
|
-
Variant that uses a specific NPC for task execution.
|
|
217
|
-
|
|
218
|
-
This allows benchmarking specific NPCs like sibiji (orchestrator),
|
|
219
|
-
corca (coding), or custom NPCs.
|
|
220
|
-
|
|
221
|
-
Usage:
|
|
222
|
-
harbor run -d terminal-bench@2.0 \\
|
|
223
|
-
--agent-import-path "npcsh.benchmark:NpcshAgentWithNpc" \\
|
|
224
|
-
-m anthropic/claude-sonnet-4-20250514 -n 4
|
|
225
|
-
"""
|
|
226
|
-
|
|
227
|
-
def __init__(self, *args, npc_name: str = "sibiji", **kwargs):
|
|
228
|
-
super().__init__(*args, **kwargs)
|
|
229
|
-
self.npc_name = npc_name
|
|
230
|
-
|
|
231
|
-
@staticmethod
|
|
232
|
-
def name() -> str:
|
|
233
|
-
return "npcsh-npc"
|
|
234
|
-
|
|
235
|
-
def create_run_agent_commands(self, instruction: str) -> list:
|
|
236
|
-
"""Create commands using a specific NPC."""
|
|
237
|
-
# Wrap the instruction with explicit jinx usage directions and retry logic
|
|
238
|
-
tool_instruction = f"""You have access to jinxs including edit_file (for writing/creating files), sh (for running shell commands), and python (for running Python code).
|
|
239
|
-
|
|
240
|
-
IMPORTANT RULES:
|
|
241
|
-
1. You MUST use these jinxs to complete the task. Do NOT just output code as text - use the edit_file jinx to actually write files to disk.
|
|
242
|
-
2. After implementing a solution, you MUST verify it works by running any provided test scripts.
|
|
243
|
-
3. If a test fails or produces an error, you MUST try a DIFFERENT approach. Do not give up.
|
|
244
|
-
4. Keep trying different approaches until you succeed or have tried at least 10 different solutions.
|
|
245
|
-
5. NEVER assume success - always check the actual output of test commands.
|
|
246
|
-
|
|
247
|
-
Task: {instruction}
|
|
248
|
-
|
|
249
|
-
WORKFLOW:
|
|
250
|
-
1. Implement your solution using edit_file and sh
|
|
251
|
-
2. Run any test scripts mentioned in the task
|
|
252
|
-
3. Check the output carefully - look for "PASS", "SUCCESS", "OK" or similar
|
|
253
|
-
4. If the test failed, analyze why and try a completely different approach
|
|
254
|
-
5. Repeat until the test passes
|
|
255
|
-
|
|
256
|
-
Remember: Use edit_file to write code files. Use sh to run commands. VERIFY your solution works before concluding."""
|
|
257
|
-
|
|
258
|
-
escaped_instruction = shlex.quote(tool_instruction)
|
|
259
|
-
model_name = self.model_name
|
|
260
|
-
|
|
261
|
-
if model_name and "/" in model_name:
|
|
262
|
-
provider, model = model_name.split("/", 1)
|
|
263
|
-
elif model_name:
|
|
264
|
-
provider = os.environ.get("NPCSH_CHAT_PROVIDER", "")
|
|
265
|
-
model = model_name
|
|
266
|
-
else:
|
|
267
|
-
provider = os.environ.get("NPCSH_CHAT_PROVIDER", "")
|
|
268
|
-
model = os.environ.get("NPCSH_CHAT_MODEL", "")
|
|
269
|
-
|
|
270
|
-
provider_map = {
|
|
271
|
-
"anthropic": "anthropic",
|
|
272
|
-
"openai": "openai",
|
|
273
|
-
"google": "gemini",
|
|
274
|
-
"gemini": "gemini",
|
|
275
|
-
"deepseek": "deepseek",
|
|
276
|
-
"ollama": "ollama",
|
|
277
|
-
}
|
|
278
|
-
npcsh_provider = provider_map.get(provider.lower(), provider)
|
|
279
|
-
|
|
280
|
-
env_vars = []
|
|
281
|
-
api_key_map = {
|
|
282
|
-
"anthropic": "ANTHROPIC_API_KEY",
|
|
283
|
-
"openai": "OPENAI_API_KEY",
|
|
284
|
-
"gemini": "GOOGLE_API_KEY",
|
|
285
|
-
"deepseek": "DEEPSEEK_API_KEY",
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
for prov, env_key in api_key_map.items():
|
|
289
|
-
if env_key in os.environ:
|
|
290
|
-
env_vars.append(f'{env_key}="{os.environ[env_key]}"')
|
|
291
|
-
|
|
292
|
-
env_prefix = " ".join(env_vars) + " " if env_vars else ""
|
|
293
|
-
|
|
294
|
-
output_dir = str(self.logs_dir / "npcsh_output")
|
|
295
|
-
output_file = str(self.logs_dir / "npcsh_output" / "output.jsonl")
|
|
296
|
-
|
|
297
|
-
commands = []
|
|
298
|
-
|
|
299
|
-
commands.append(ExecInput(
|
|
300
|
-
command=f"mkdir -p {shlex.quote(output_dir)}",
|
|
301
|
-
timeout_sec=30
|
|
302
|
-
))
|
|
303
|
-
|
|
304
|
-
# Create .npcsh_global file to use global team and avoid interactive prompts
|
|
305
|
-
commands.append(ExecInput(
|
|
306
|
-
command="touch /app/.npcsh_global",
|
|
307
|
-
timeout_sec=10
|
|
308
|
-
))
|
|
309
|
-
|
|
310
|
-
# Use specific NPC with --npc flag
|
|
311
|
-
# NPCSH_DEFAULT_MODE=agent enables automatic tool execution
|
|
312
|
-
npcsh_cmd = (
|
|
313
|
-
f'{env_prefix}'
|
|
314
|
-
f'NPCSH_CHAT_MODEL="{model}" '
|
|
315
|
-
f'NPCSH_CHAT_PROVIDER="{npcsh_provider}" '
|
|
316
|
-
f'NPCSH_STREAM_OUTPUT=0 '
|
|
317
|
-
f'NPCSH_DEFAULT_MODE=agent '
|
|
318
|
-
f'npc --npc {self.npc_name} {escaped_instruction} '
|
|
319
|
-
f'2>&1 | tee {shlex.quote(output_file)}'
|
|
320
|
-
)
|
|
321
|
-
|
|
322
|
-
commands.append(ExecInput(
|
|
323
|
-
command=npcsh_cmd,
|
|
324
|
-
timeout_sec=600,
|
|
325
|
-
))
|
|
326
|
-
|
|
327
|
-
return commands
|
|
@@ -12,10 +12,18 @@ if ! command -v pip &> /dev/null; then
|
|
|
12
12
|
apt-get update && apt-get install -y python3-pip
|
|
13
13
|
fi
|
|
14
14
|
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
# Upgrade pip first to handle modern packages and extras
|
|
16
|
+
echo "Upgrading pip..."
|
|
17
|
+
pip install --upgrade pip 2>/dev/null || pip install --break-system-packages --upgrade pip 2>/dev/null || true
|
|
18
|
+
|
|
19
|
+
# Install from local source (uploaded by agent setup)
|
|
20
|
+
echo "Installing npcpy from local source..."
|
|
21
|
+
if [ -d /src/npcpy ]; then
|
|
22
|
+
pip install --break-system-packages -e /src/npcpy 2>/dev/null || pip install -e /src/npcpy
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
echo "Installing npcsh from local source..."
|
|
26
|
+
pip install --break-system-packages -e /src/npcsh ollama 2>/dev/null || pip install -e /src/npcsh ollama
|
|
19
27
|
|
|
20
28
|
# Verify installation
|
|
21
29
|
echo "Verifying npcsh installation..."
|
npcsh/config.py
CHANGED
|
@@ -28,9 +28,9 @@ NPCSH_DEFAULT_MODE = os.environ.get("NPCSH_DEFAULT_MODE", "agent")
|
|
|
28
28
|
NPCSH_VISION_MODEL = os.environ.get("NPCSH_VISION_MODEL", "gemma3:4b")
|
|
29
29
|
NPCSH_VISION_PROVIDER = os.environ.get("NPCSH_VISION_PROVIDER", "ollama")
|
|
30
30
|
NPCSH_IMAGE_GEN_MODEL = os.environ.get(
|
|
31
|
-
"NPCSH_IMAGE_GEN_MODEL", "
|
|
31
|
+
"NPCSH_IMAGE_GEN_MODEL", "x/z-image-turbo"
|
|
32
32
|
)
|
|
33
|
-
NPCSH_IMAGE_GEN_PROVIDER = os.environ.get("NPCSH_IMAGE_GEN_PROVIDER", "
|
|
33
|
+
NPCSH_IMAGE_GEN_PROVIDER = os.environ.get("NPCSH_IMAGE_GEN_PROVIDER", "ollama")
|
|
34
34
|
NPCSH_VIDEO_GEN_MODEL = os.environ.get(
|
|
35
35
|
"NPCSH_VIDEO_GEN_MODEL", "damo-vilab/text-to-video-ms-1.7b"
|
|
36
36
|
)
|
|
@@ -44,6 +44,9 @@ NPCSH_API_URL = os.environ.get("NPCSH_API_URL", None)
|
|
|
44
44
|
NPCSH_SEARCH_PROVIDER = os.environ.get("NPCSH_SEARCH_PROVIDER", "duckduckgo")
|
|
45
45
|
NPCSH_BUILD_KG = os.environ.get("NPCSH_BUILD_KG", "1") != "0"
|
|
46
46
|
NPCSH_EDIT_APPROVAL = os.environ.get("NPCSH_EDIT_APPROVAL", "off") # off, interactive, auto
|
|
47
|
+
NPCSH_TTS_ENGINE = os.environ.get("NPCSH_TTS_ENGINE", "")
|
|
48
|
+
NPCSH_TTS_VOICE = os.environ.get("NPCSH_TTS_VOICE", "")
|
|
49
|
+
NPCSH_YAP_SETUP_DONE = os.environ.get("NPCSH_YAP_SETUP_DONE", "0") == "1"
|
|
47
50
|
|
|
48
51
|
|
|
49
52
|
def get_shell_config_file() -> str:
|
npcsh/mcp_server.py
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
|
|
2
2
|
"""
|
|
3
|
-
Enhanced MCP server that incorporates functionality from npcpy.routes,
|
|
3
|
+
Enhanced MCP server that incorporates functionality from npcpy.routes,
|
|
4
4
|
npcpy.llm_funcs, and npcpy.npc_compiler as tools.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
# When run as a subprocess, Python adds the script directory to sys.path[0].
|
|
8
|
+
# Since this file lives inside the npcsh package, that shadows the package
|
|
9
|
+
# (npcsh.py is found instead of the npcsh/ package). Remove it.
|
|
10
|
+
import sys as _sys, os as _os
|
|
11
|
+
_script_dir = _os.path.dirname(_os.path.abspath(__file__))
|
|
12
|
+
if _script_dir in _sys.path:
|
|
13
|
+
_sys.path.remove(_script_dir)
|
|
14
|
+
|
|
7
15
|
import os
|
|
8
16
|
import subprocess
|
|
9
17
|
import json
|
npcsh/npc_team/alicanto.npc
CHANGED
|
@@ -11,13 +11,15 @@ colors:
|
|
|
11
11
|
top: "255,215,0"
|
|
12
12
|
bottom: "218,165,32"
|
|
13
13
|
primary_directive: |
|
|
14
|
-
You are alicanto, the research
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
You are alicanto, the deep research specialist. You produce thorough research reports.
|
|
15
|
+
Search multiple sources, read papers, gather evidence, then synthesize into a report.
|
|
16
|
+
Use web_search and file_search to find sources. Use python to analyze data.
|
|
17
|
+
Say RESEARCH_COMPLETE when you have enough evidence to answer.
|
|
18
18
|
jinxs:
|
|
19
|
-
- lib/core/search
|
|
19
|
+
- lib/core/search/web_search
|
|
20
|
+
- lib/core/search/file_search
|
|
21
|
+
- lib/core/search/db_search
|
|
20
22
|
- lib/core/sh
|
|
21
23
|
- lib/core/python
|
|
22
24
|
- lib/core/load_file
|
|
23
|
-
- lib/
|
|
25
|
+
- lib/core/edit_file
|
npcsh/npc_team/corca.npc
CHANGED
|
@@ -11,21 +11,14 @@ colors:
|
|
|
11
11
|
top: "64,224,208"
|
|
12
12
|
bottom: "255,165,0"
|
|
13
13
|
primary_directive: |
|
|
14
|
-
You are corca, the software development
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
CRITICAL: You MUST ALWAYS use FULL ABSOLUTE PATHS for all file operations.
|
|
21
|
-
- NEVER use relative paths like "apps/api" or "./src"
|
|
22
|
-
- ALWAYS expand paths starting from root like "/Users/username/project/apps/api"
|
|
23
|
-
- When given a task, first determine the absolute path of the working directory using pwd
|
|
24
|
-
- Prefix all file paths with the full absolute path
|
|
14
|
+
You are corca, the software development and shell specialist.
|
|
15
|
+
You run commands, write code, debug, edit files, and handle system tasks.
|
|
16
|
+
Always use FULL ABSOLUTE PATHS. Run pwd first if you need the working directory.
|
|
17
|
+
When counting things, use wc -l. When debugging, read the error, fix it, retry.
|
|
18
|
+
Keep it simple. Do the task, report the result.
|
|
25
19
|
jinxs:
|
|
26
20
|
- lib/core/sh
|
|
27
21
|
- lib/core/python
|
|
28
22
|
- lib/core/edit_file
|
|
29
23
|
- lib/core/load_file
|
|
30
|
-
- lib/core/search
|
|
31
24
|
|
npcsh/npc_team/frederic.npc
CHANGED
|
@@ -11,17 +11,14 @@ colors:
|
|
|
11
11
|
top: "224,255,255"
|
|
12
12
|
bottom: "173,216,230"
|
|
13
13
|
primary_directive: |
|
|
14
|
-
You are frederic the polar bear
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
You help users with hard math, physics problems, and music composition.
|
|
19
|
-
Cut through the ice to get to what matters. Make the complex feel simple and beautiful.
|
|
14
|
+
You are frederic the polar bear — Feynman's curiosity meets Chopin's soul.
|
|
15
|
+
Make the complex simple and beautiful. Find elegance in structure.
|
|
16
|
+
Use analogies, strip away jargon, show why things are beautiful not just correct.
|
|
17
|
+
Use python to demonstrate ideas visually when math is involved.
|
|
20
18
|
jinxs:
|
|
21
19
|
- lib/core/python
|
|
22
20
|
- lib/core/sql
|
|
23
21
|
- lib/core/sh
|
|
24
22
|
- lib/core/load_file
|
|
25
|
-
- lib/core/search
|
|
26
|
-
- lib/
|
|
27
|
-
- bin/wander
|
|
23
|
+
- lib/core/search/web_search
|
|
24
|
+
- lib/core/edit_file
|
npcsh/npc_team/guac.npc
CHANGED
|
@@ -11,10 +11,10 @@ ascii_art: |
|
|
|
11
11
|
🟢 🟢 🟢 🟢 ⚫🥑🍅⚫ 🟢
|
|
12
12
|
🟢🟢🟢🟢🟢🟢 🟢🟢🟢🟢 ⚫⚫🟢 🟢🟢🟢
|
|
13
13
|
primary_directive: |
|
|
14
|
-
You are guac, the data analysis specialist
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
You are guac, the data analysis specialist.
|
|
15
|
+
You load, analyze, and visualize data with pandas, numpy, matplotlib, and SQL.
|
|
16
|
+
Load data first, inspect it, do the analysis, report results with numbers.
|
|
17
|
+
For plots, save to file with plt.savefig() and report the path.
|
|
18
18
|
jinxs:
|
|
19
19
|
- lib/core/python
|
|
20
20
|
- lib/core/sql
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
jinx_name: delegate
|
|
2
|
-
description:
|
|
2
|
+
description: ONLY for complex multi-step tasks. Sends a task to a specialist NPC who works on it with feedback until done. Do NOT use this for simple commands — use sh or python instead and answer directly.
|
|
3
3
|
inputs:
|
|
4
4
|
- npc_name:
|
|
5
5
|
description: "Name of the NPC to delegate to"
|