Skip to main content

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.15

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for shalya 0.1.15
File Size Uploaded Provenance
shalya-0.1.15.tar.gz 60.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for shalya 0.1.15
File Interpreter ABI Platform
shalya-0.1.15-py3-none-any.whl Python 3 none any Details

Total release size: 125.3 kB

Release files / shalya-0.1.15.tar.gz

Download URL shalya-0.1.15.tar.gz
Size 60.9 kB
Tags Source
SHA-256 checksum
How to use checksums
f799c7d441ba59028203257a49aecb9e849923f16556a1431fa846b2330b776e
BLAKE2b-256 checksum
How to use checksums
6845f608b3fb51848d5309f5de224c61520de444da94c465c98d8b7c2246cfc9
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.15-py3-none-any.whl

Download URL shalya-0.1.15-py3-none-any.whl
Size 64.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5ad6cdeb793795ab1279e0d2dc4d48483ef8b707a69feab620cf5b4fa27ae81c
BLAKE2b-256 checksum
How to use checksums
d0424261b8cee6e732c690d70a5034de8860afe51690b504d83d47e87c101150
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.15

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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page