shalya
An LLM agent works by calling tools: read this file, search for that function, run this command. shalya (शल्य) provides those tools as plain Python functions, each with the type hints and docstring a model reads, each returning the text the model sees next. It does not run an agent loop or talk to a model. A harness such as ramabana does that, and asks shalya for the tools.
Two ideas carry the package. A host is the application the agent works inside: the folders it may touch, and whatever else it offers, such as a shell, a Python session, web access or a memory store. A host declares what it supports as capability groups, such as code, web or shell. tools_for(host) builds the tools of every group the host supports, bound to that host, and returns them as one list. A host that cannot serve a group simply gets no tools for it, so a model is never offered a tool that cannot work.
Install
pip install shalya
from shalya.core import failed
from shalya.host import LocalHost
from shalya.tools import tools_for, read_only
from shalya.skills import Skill, Registry, skill_index
A host
LocalHost is the host for folders on your own machine. It reads and writes only inside the folders you open, called roots. can(group) asks whether it supports a group, and without lists the groups this instance has to go without. Here memory and API specifications are missing because no backend was given, and web access was switched off.
import shutil
from pathlib import Path
d = Path('/tmp/shalya-demo')
shutil.rmtree(d, ignore_errors=True); d.mkdir(parents=True)
(d/'greet.py').write_text('def hi(n): return f"hi {n}"\n')
host = LocalHost(roots=[d], index=False, web=False)
host.can('file'), host.can('memory'), sorted(host.without)
(True, False, ['api', 'ask', 'memory', 'watch', 'web'])
The tools
tools_for returns one flat list, built from the groups this host supports. Every tool takes strings and simple values and returns a string, so a harness can pass the list to a model without knowing anything about hosts.
ts = {t.__name__: t for t in tools_for(host)}
len(ts), sorted(ts)
(28,
['add_cell',
'add_root',
'create_file',
'edit_cell',
'git_checkout',
'git_commit',
'git_diff',
'git_divergence',
'git_log',
'git_remote',
'git_stash',
'git_status',
'grep',
'inspect_python',
'ls',
'notebook_cells',
'outline',
'read_terminal',
'replace_text',
'restart_kernel',
'run_python',
'run_shell',
'run_shell_bg',
'search_code',
'shell_output',
'shell_stop',
'view_cell',
'view_file'])
A tool answers with the text a model reads. view_file shows a file with a line number and a short hash of each line. replace_text edits by exact text: it takes a list of {oldText, newText} pairs, each oldText must match exactly once, and either all of them apply or none does. edit_file, an editor that addresses lines by their hashes and refuses a stale address, is the opt-in exhash group: tools_for(host, optin=('exhash',)).
print(ts['view_file']('greet.py'))
1|f8c6|def hi(n): return f"hi {n}"
print(ts['replace_text']('greet.py', [{'oldText': 'hi {n}', 'newText': 'hey {n}'}]))
replaced 1 block(s) in /private/tmp/shalya-demo/greet.py
--- a//private/tmp/shalya-demo/greet.py
+++ b//private/tmp/shalya-demo/greet.py
@@ -1 +1 @@
-def hi(n): return f"hi {n}"
+def hi(n): return f"hey {n}"
Tools never raise, because an exception would end the agent’s turn. A tool that cannot do what was asked returns a string beginning with ERROR:, and failed is the function that checks for it.
r = ts['view_file']('missing.py')
failed(r), r
(True, 'ERROR: no such file: /private/tmp/shalya-demo/missing.py')
A browser
LocalHost has no browser. A host that inherits BrowserHost can offer the opt-in browser group (optin=('browser',)). browse, screenshot, page_text and page_reload look at a page without changing anything. page_click, page_type and page_eval act on the page, so they count as writes and go to a person for approval. screenshot returns the path of the PNG it saved, because a tool result is text.
Opting in
optin names the groups nobody gets by default: exhash (edit_file), research (a cited digest of the top web results), author (create_skill), browser (see above) and legacy. The legacy group is one release of shims for old tool names an MCP client may still send: list_files, list_vars, environment, memory_tree, git_rebase_preview, set_reminder and watch_url. They are removed in 0.2.0. An opt-in group the host cannot back is empty, not broken.
sorted({t.__name__ for t in tools_for(host, optin=('exhash', 'legacy'))} - set(ts))
['edit_file', 'environment', 'git_rebase_preview', 'list_files', 'list_vars']
A host with a memory backend, a vishalakshi vault (0.1.17 or later), gets the memory and watch tools too. memory_read(ref='') browses memory: with no argument it lists every document, with a document it shows its headings, and with a section id such as doc#3 it returns that section. remember(key=...) replaces the note filed earlier under the same key. One watch(target, kind=...) tool re-checks a url, repeats a web search, watches a folder, or files a reminder on a schedule.
The git tools that change a repository answer in JSON: gheasy’s summary, the new head, whether the branch moved, and an undo token a harness can use to rewind what a turn did.
Tools for a sub-agent
A sub-agent sent to look something up should not change anything. read_only filters a tool list down to what it may have, dropping every write tool unless the tool offers a read-only version of itself. read_url offers one, which reads a page without saving it to memory. The cell below lists the tools read_only removed.
sorted(set(ts) - {t.__name__ for t in read_only(ts.values())})
['add_cell',
'add_root',
'create_file',
'edit_cell',
'git_checkout',
'git_commit',
'git_remote',
'git_stash',
'replace_text',
'restart_kernel',
'run_python',
'run_shell',
'run_shell_bg']
Skills
A skill is a document of instructions for one kind of task. skill_index renders the block that goes in the system prompt: each skill’s name and a clipped description, never its body, which the agent reads with read_skill when it needs it. discover collects the skills installed packages publish as pyskills and every SKILL.md under the open folders.
ks = [Skill(name='exhash', source='md', description='Hash-verified text editing.', where='', _text='View, then edit by address.'),
Skill(name='ghapi', source='md', description='GitHub REST access through `GhApi`.', where='', _text='Issues, pull requests, releases.')]
print(skill_index(ks))
## Skills
Use `read_skill(name)` before doing the work a skill covers. Installed tool code is also searchable with `search_code`.
- `exhash`: Hash-verified text editing.
- `ghapi`: GitHub REST access through `GhApi`.
Extensions
An extension is a Python file with a setup(reg) function. Through reg, a Registry, it can add tools, skills, slash commands, lifecycle hooks and an approval policy. Hooking an event name that does not exist raises an error, so a typo cannot silently hook nothing.
reg = Registry()
@reg.tool
def weather(city: str) -> str:
"Today's weather in `city`."
return f'sunny in {city}'
reg.skill('house-style', 'Short sentences. No headings.')
[t.__name__ for t in reg.tools], [s.name for s in reg.skills]
(['weather'], ['house-style'])
Develop
uv sync --all-extras --group dev
uv run nbdev-export
uv run nbdev-test
uv run pytest
uv run nbdev-clean
Metadata
Release files for shalya 0.1.17
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| shalya-0.1.17.tar.gz | 61.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| shalya-0.1.17-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 127.3 kB
Release files / shalya-0.1.17.tar.gz
| Download URL | shalya-0.1.17.tar.gz |
|---|---|
| Size | 61.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
072b381039122364d26c02264540e429e07fdb988875c043666284ab4fd18572
|
|
BLAKE2b-256 checksum How to use checksums |
ce7b9c5a2dd34b0f3d28b38b3e4b344e4d5c7b71460e7530b289b105d02d0a8d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / shalya-0.1.17-py3-none-any.whl
| Download URL | shalya-0.1.17-py3-none-any.whl |
|---|---|
| Size | 65.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7cc563dad5a7e619817e0a7571a57a2b9d4055101bd875f42db5c42506ffdbd7
|
|
BLAKE2b-256 checksum How to use checksums |
8f375f688797dc4a7cec58df6b4c3da83cc5e6afa6d6a77088cd057d4a6953b5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Security for shalya 0.1.17
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.