Metadata-Version: 2.5
Name: shalya
Version: 0.0.7
Summary: the tools an agent is given, and the host that answers them
Project-URL: Repository, https://github.com/vedicreader/shalya
Project-URL: Documentation, https://vedicreader.github.io/shalya/
Author-email: Karthik <karthik.rajgopal@hotmail.com>
License: Apache-2.0
License-File: LICENSE
Keywords: nbdev
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.12
Requires-Dist: dhrishti
Requires-Dist: exhash>=0.4.9
Requires-Dist: fastcore>=2.1.16
Requires-Dist: fastspec
Requires-Dist: fossick>=0.1.17
Requires-Dist: gheasy
Requires-Dist: jupyter-client
Requires-Dist: litesearch>=0.1.34
Requires-Dist: vishalakshi
Description-Content-Type: text/markdown

# shalya


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

A host is an object that answers what it can: read a file, search an index, run a command, remember a page. [`tools_for`](https://vedicreader.github.io/shalya/tools.html#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

``` sh
pip install shalya
```

``` python
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`](https://vedicreader.github.io/shalya/host.html#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.

``` python
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`](https://vedicreader.github.io/shalya/tools.html#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.

``` python
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.

``` python
print(ts['view_file']('greet.py'))
```

    1|f8c6|def hi(n): return f"hi {n}"

``` python
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`](https://vedicreader.github.io/shalya/core.html#failed) is the one place that knows that spelling.

``` python
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`](https://vedicreader.github.io/shalya/tools.html#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.

``` python
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`](https://vedicreader.github.io/shalya/skills.html#skill_index) renders the block that goes in the system prompt: names and clipped descriptions, never bodies. [`discover`](https://vedicreader.github.io/shalya/skills.html#discover) collects installed pyskills and every `SKILL.md` under the open folders.

``` python
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.

``` python
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

``` sh
uv sync --all-extras --group dev
uv run nbdev-export
uv run nbdev-test
uv run pytest
uv run nbdev-clean
```
