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)
(25,
['add_cell',
'add_root',
'create_file',
'edit_cell',
'edit_file',
'git_checkout',
'git_divergence',
'git_rebase_preview',
'git_remote',
'git_status',
'grep',
'inspect_python',
'list_files',
'list_vars',
'ls',
'notebook_cells',
'outline',
'read_terminal',
'replace_text',
'run_python',
'run_shell',
'search_code',
'similar_code',
'view_cell',
'view_file'])
A tool answers with the text a model reads. view_file numbers and hashes each line, and that hash is the address edit_file takes.
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 beginning ERROR:, and failed is the one place that knows that spelling.
r = ts['view_file']('missing.py')
failed(r), r
(True, 'ERROR: no such file: /private/tmp/shalya-demo/missing.py')
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',
'edit_file',
'git_checkout',
'git_remote',
'replace_text',
'run_python',
'run_shell']
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
Know-how available to you. Read one with `read_skill(name)` when its description matches what you are about to do, *before* you do it -- several of these describe tools already installed in this environment, so the code they discuss 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
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shalya-0.0.4.tar.gz.
File metadata
- Download URL: shalya-0.0.4.tar.gz
- Upload date:
- Size: 41.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
012c09065e3f87cf02c346e7a5e5efb963cf6fbf26ab2fd2305ad137e31fc964
|
|
| MD5 |
284e6b6688effece0276f89600a7fb45
|
|
| BLAKE2b-256 |
caabc15f344c885120df24a5e6422ad160c478794681800e372b883a50f5be96
|
File details
Details for the file shalya-0.0.4-py3-none-any.whl.
File metadata
- Download URL: shalya-0.0.4-py3-none-any.whl
- Upload date:
- Size: 44.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01fdb2ba4825081e916ee12d0fd4d1a65bf96d2c19710f486a5f6887db013f0e
|
|
| MD5 |
aa0c1263787bd27f643444f3e3af4837
|
|
| BLAKE2b-256 |
ee0359f4e780e3defc16c56d885812097a79d3d785bd41ddb696f96c5d94d431
|