shalya
A host is an object that answers what it can: read a file, search an index, run a command, remember a page. tools_for asks one what it supports and hands back those tools as plain functions, each with the type hints and docstring a model reads. Nothing here runs an agent loop or talks to a model.
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 touches only the folders you open, and reports the capability groups it can serve. A group with no installed backend goes absent rather than broken.
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 answers. Every tool takes and returns strings: a harness can pass them to a model without knowing what a host is.
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 numbers and hashes each line; replace_text edits by exact text, a list of {oldText, newText} that all apply or none do; edit_file, the hash-addressed editor, 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}"
A refusal is a string that begins with ERROR:. failed is the one 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 and act without writing; page_click, page_type and page_eval act on it and go to a person. 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, one release of shims for the names an MCP client may still send (list_files, list_vars, environment, memory_tree, git_rebase_preview, set_reminder, watch_url; gone in 0.2.0). An opt-in the host cannot back is empty rather than 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']
With a vault (vishalakshi 0.1.17) the host also answers memory_read(ref=''), which browses the roots, a document’s headings or one section by doc#n; remember(key=), which replaces the note filed under the same key; and one watch(target, kind=) for a url, a reminder, a web search or a folder. Git writes answer in JSON with gheasy’s undo token beside summary, head and moved, so a harness can rewind what a turn did.
Tools for a sub-agent
read_only filters a list for a sub-agent that must not change anything. It drops every write tool, unless that tool publishes a read-only twin. read_url publishes one, and the twin reads a page without saving it to memory.
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
skill_index renders the block that goes in the system prompt: names and clipped descriptions, never bodies. discover collects installed 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
A Python file with setup(reg) may add tools, skills, lifecycle hooks and an approval policy. An unknown event name raises rather than hooking 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.10
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.10.tar.gz | 58.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| shalya-0.1.10-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 120.9 kB
Release files / shalya-0.1.10.tar.gz
| Download URL | shalya-0.1.10.tar.gz |
|---|---|
| Size | 58.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0ae65fc87fb49af269cb3837483d714335500d1f7af1dab473e2b034e0c4cba3
|
|
BLAKE2b-256 checksum How to use checksums |
4696ecae41455e48860dd85daca108673bd6a19e23fe93382fb41fb8da505596
|
| 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.10-py3-none-any.whl
| Download URL | shalya-0.1.10-py3-none-any.whl |
|---|---|
| Size | 62.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5b7ccd411947c1b64ee482d6418c1adfac462976c9742a508105bd7625e37c54
|
|
BLAKE2b-256 checksum How to use checksums |
0adb901d0f7902b71b9d1a046d5bb3ae0793821e7024555de5748f436a95db74
|
| 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.10
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.