curry-leaves 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- curry_leaves-1.0.0/.gitignore +34 -0
- curry_leaves-1.0.0/CHANGELOG.md +41 -0
- curry_leaves-1.0.0/CONTRIBUTING.md +166 -0
- curry_leaves-1.0.0/LICENSE +21 -0
- curry_leaves-1.0.0/PKG-INFO +392 -0
- curry_leaves-1.0.0/README.md +357 -0
- curry_leaves-1.0.0/assets/logo.png +0 -0
- curry_leaves-1.0.0/examples/01_basic.py +47 -0
- curry_leaves-1.0.0/examples/02_streaming.py +63 -0
- curry_leaves-1.0.0/examples/03_custom_tool.py +96 -0
- curry_leaves-1.0.0/examples/04_structured_output.py +71 -0
- curry_leaves-1.0.0/examples/05_subagents.py +66 -0
- curry_leaves-1.0.0/examples/06_host_and_permissions.py +120 -0
- curry_leaves-1.0.0/examples/07_mcp_tools.py +120 -0
- curry_leaves-1.0.0/publish.sh +106 -0
- curry_leaves-1.0.0/pyproject.toml +67 -0
- curry_leaves-1.0.0/src/curry_leaves/__init__.py +362 -0
- curry_leaves-1.0.0/src/curry_leaves/agents.py +103 -0
- curry_leaves-1.0.0/src/curry_leaves/catalog.py +222 -0
- curry_leaves-1.0.0/src/curry_leaves/cli/__init__.py +0 -0
- curry_leaves-1.0.0/src/curry_leaves/cli/chat.py +313 -0
- curry_leaves-1.0.0/src/curry_leaves/cli/host.py +55 -0
- curry_leaves-1.0.0/src/curry_leaves/cli/render.py +132 -0
- curry_leaves-1.0.0/src/curry_leaves/cli/theme.py +24 -0
- curry_leaves-1.0.0/src/curry_leaves/cli/tui/__init__.py +0 -0
- curry_leaves-1.0.0/src/curry_leaves/cli/tui/app.py +564 -0
- curry_leaves-1.0.0/src/curry_leaves/cli/tui/controller.py +135 -0
- curry_leaves-1.0.0/src/curry_leaves/cli/tui/figlet.py +33 -0
- curry_leaves-1.0.0/src/curry_leaves/cli/tui/host.py +66 -0
- curry_leaves-1.0.0/src/curry_leaves/cli/tui/logo_image.py +52 -0
- curry_leaves-1.0.0/src/curry_leaves/cli/tui/state.py +389 -0
- curry_leaves-1.0.0/src/curry_leaves/cli/tui/widgets.py +370 -0
- curry_leaves-1.0.0/src/curry_leaves/compaction.py +216 -0
- curry_leaves-1.0.0/src/curry_leaves/core/__init__.py +0 -0
- curry_leaves-1.0.0/src/curry_leaves/core/agent.py +204 -0
- curry_leaves-1.0.0/src/curry_leaves/core/blobs.py +44 -0
- curry_leaves-1.0.0/src/curry_leaves/core/events.py +291 -0
- curry_leaves-1.0.0/src/curry_leaves/core/host.py +115 -0
- curry_leaves-1.0.0/src/curry_leaves/core/loop.py +236 -0
- curry_leaves-1.0.0/src/curry_leaves/core/messages.py +228 -0
- curry_leaves-1.0.0/src/curry_leaves/core/tools.py +395 -0
- curry_leaves-1.0.0/src/curry_leaves/mcp/__init__.py +41 -0
- curry_leaves-1.0.0/src/curry_leaves/mcp/client.py +151 -0
- curry_leaves-1.0.0/src/curry_leaves/mcp/config.py +132 -0
- curry_leaves-1.0.0/src/curry_leaves/mcp/manager.py +122 -0
- curry_leaves-1.0.0/src/curry_leaves/mcp/pick.py +50 -0
- curry_leaves-1.0.0/src/curry_leaves/mcp/server.py +183 -0
- curry_leaves-1.0.0/src/curry_leaves/mcp/tool.py +144 -0
- curry_leaves-1.0.0/src/curry_leaves/permission.py +193 -0
- curry_leaves-1.0.0/src/curry_leaves/presets.py +45 -0
- curry_leaves-1.0.0/src/curry_leaves/prompt.py +177 -0
- curry_leaves-1.0.0/src/curry_leaves/providers/__init__.py +0 -0
- curry_leaves-1.0.0/src/curry_leaves/providers/anthropic.py +297 -0
- curry_leaves-1.0.0/src/curry_leaves/providers/base.py +174 -0
- curry_leaves-1.0.0/src/curry_leaves/providers/factory.py +66 -0
- curry_leaves-1.0.0/src/curry_leaves/providers/openai.py +320 -0
- curry_leaves-1.0.0/src/curry_leaves/providers/sse.py +45 -0
- curry_leaves-1.0.0/src/curry_leaves/py.typed +0 -0
- curry_leaves-1.0.0/src/curry_leaves/runner.py +701 -0
- curry_leaves-1.0.0/src/curry_leaves/session/__init__.py +17 -0
- curry_leaves-1.0.0/src/curry_leaves/session/store.py +303 -0
- curry_leaves-1.0.0/src/curry_leaves/settings.py +175 -0
- curry_leaves-1.0.0/src/curry_leaves/skills.py +118 -0
- curry_leaves-1.0.0/src/curry_leaves/thinking.py +135 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/__init__.py +0 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/ask.py +51 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/bash.py +120 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/current_time.py +58 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/edit.py +98 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/find.py +74 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/glob.py +78 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/read.py +84 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/search.py +107 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/search_tools.py +50 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/task.py +58 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/tasks.py +241 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/transfer.py +53 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/web.py +146 -0
- curry_leaves-1.0.0/src/curry_leaves/tools/write.py +55 -0
- curry_leaves-1.0.0/src/curry_leaves/util/__init__.py +0 -0
- curry_leaves-1.0.0/src/curry_leaves/util/frontmatter.py +21 -0
- curry_leaves-1.0.0/src/curry_leaves/util/paths.py +103 -0
- curry_leaves-1.0.0/src/curry_leaves/util/resources.py +61 -0
- curry_leaves-1.0.0/src/curry_leaves/util/retry.py +56 -0
- curry_leaves-1.0.0/tests/mcp/conftest.py +54 -0
- curry_leaves-1.0.0/tests/mcp/fixtures/echo_server.py +31 -0
- curry_leaves-1.0.0/tests/mcp/fixtures/http_echo_server.py +32 -0
- curry_leaves-1.0.0/tests/mcp/test_agent_integration.py +85 -0
- curry_leaves-1.0.0/tests/mcp/test_client_stdio.py +55 -0
- curry_leaves-1.0.0/tests/mcp/test_config.py +150 -0
- curry_leaves-1.0.0/tests/mcp/test_manager.py +105 -0
- curry_leaves-1.0.0/tests/mcp/test_pick.py +88 -0
- curry_leaves-1.0.0/tests/mcp/test_server.py +61 -0
- curry_leaves-1.0.0/tests/mcp/test_tool_adapter.py +152 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Python bytecode and caches
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Build and packaging artifacts
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.eggs/
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
env/
|
|
16
|
+
|
|
17
|
+
# Test and coverage artifacts
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
.coverage.*
|
|
21
|
+
htmlcov/
|
|
22
|
+
|
|
23
|
+
# Type checker and linter caches
|
|
24
|
+
.mypy_cache/
|
|
25
|
+
.ruff_cache/
|
|
26
|
+
.pyre/
|
|
27
|
+
|
|
28
|
+
# IDE and OS files
|
|
29
|
+
.vscode/
|
|
30
|
+
.idea/
|
|
31
|
+
.DS_Store
|
|
32
|
+
|
|
33
|
+
# Local agent-tooling state
|
|
34
|
+
.claude/
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2026-07-08
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial release of Curry Leaves for Python — a provider-agnostic, multi-agent
|
|
13
|
+
kernel for building AI agents: streaming tool-use loop, subagents, skills,
|
|
14
|
+
thinking, permissions, sessions, and compaction. A faithful port of the
|
|
15
|
+
[TypeScript kernel](https://github.com/ilayanambi-ponramu/curry-leaves-ts),
|
|
16
|
+
module-for-module.
|
|
17
|
+
- Core engine (`agent_loop`), stateful driver (`Runner`), and stateless agent
|
|
18
|
+
definitions (`Agent`), with agent-as-tool delegation (`task`) and one-way
|
|
19
|
+
handoff (`transfer`).
|
|
20
|
+
- Providers for Anthropic, OpenAI, and Ollama (OpenAI-compatible), with
|
|
21
|
+
automatic provider inference from model id.
|
|
22
|
+
- Tool system with pydantic schemas, risk-based permission fallback, deferred
|
|
23
|
+
tool discovery via `search_tools`, and blob-backed result capping.
|
|
24
|
+
- **MCP (Model Context Protocol) client support** — connect stdio or HTTP MCP
|
|
25
|
+
servers (`McpServerStdio` / `McpServerHttp` / `McpServerManager`), pick
|
|
26
|
+
specific tools with `mcp_tools()`, or load server definitions from
|
|
27
|
+
`settings.json`'s `mcpServers` key. Goes beyond the TypeScript sibling, which
|
|
28
|
+
lists MCP as a non-goal.
|
|
29
|
+
- Permission engine with per-call resolution (deny > per-tool allow > standing
|
|
30
|
+
approval > per-tool ask > default > risk fallback).
|
|
31
|
+
- Auto-thinking (reasoning-effort classifier) and auto-compaction near context
|
|
32
|
+
limits.
|
|
33
|
+
- Skills system with progressive disclosure from `~/.curry-leaves/skills/` and
|
|
34
|
+
`.curry-leaves/skills/`.
|
|
35
|
+
- Model catalog sourced from models.dev (context windows, pricing).
|
|
36
|
+
- Session recording to `<home>/sessions/<id>/`.
|
|
37
|
+
- Two bundled CLIs: a full-screen Textual TUI (`curry-leaves`, alias `curry`)
|
|
38
|
+
and a line REPL (`curry-leaves-repl`).
|
|
39
|
+
- Example scripts covering basic usage, streaming, custom tools, structured
|
|
40
|
+
output, subagents, host/permissions, and MCP tools.
|
|
41
|
+
- Test suite for the MCP subsystem (`tests/mcp`); strict `mypy` across `src/`.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Contributing to curry-leaves
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in improving **curry-leaves** — bug reports, features, docs, and tests are all
|
|
4
|
+
welcome. This guide covers how to get set up, the conventions that keep the codebase coherent, and
|
|
5
|
+
how to extend the kernel.
|
|
6
|
+
|
|
7
|
+
By participating, you agree to keep interactions respectful and constructive.
|
|
8
|
+
|
|
9
|
+
## Table of contents
|
|
10
|
+
|
|
11
|
+
- [Ways to contribute](#ways-to-contribute)
|
|
12
|
+
- [Development setup](#development-setup)
|
|
13
|
+
- [Project layout](#project-layout)
|
|
14
|
+
- [Pull request workflow](#pull-request-workflow)
|
|
15
|
+
- [Code conventions](#code-conventions)
|
|
16
|
+
- [Extending the kernel](#extending-the-kernel)
|
|
17
|
+
- [Commit messages](#commit-messages)
|
|
18
|
+
- [Reporting bugs](#reporting-bugs)
|
|
19
|
+
- [License](#license)
|
|
20
|
+
|
|
21
|
+
## Ways to contribute
|
|
22
|
+
|
|
23
|
+
- **Report a bug** — open an issue with a minimal reproduction (see [Reporting bugs](#reporting-bugs)).
|
|
24
|
+
- **Propose a feature** — open an issue describing the use case *before* writing code, so we can
|
|
25
|
+
agree on the approach.
|
|
26
|
+
- **Improve docs** — READMEs, code comments, and examples all count.
|
|
27
|
+
- **Add tests** — the MCP subsystem has a suite under `tests/mcp`; extending coverage to the core
|
|
28
|
+
loop, providers, and tools is a high-value contribution.
|
|
29
|
+
|
|
30
|
+
## Development setup
|
|
31
|
+
|
|
32
|
+
Requires **Python 3.11+**.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
git clone https://github.com/ilayanambi-ponramu/curry-leaves-py.git
|
|
36
|
+
cd curry-leaves-py
|
|
37
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
38
|
+
pip install -e ".[dev]"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Common commands:
|
|
42
|
+
|
|
43
|
+
| Command | What it does |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `mypy src` | Strict type check — **the correctness gate** |
|
|
46
|
+
| `pytest` | Run the test suite (`tests/`) |
|
|
47
|
+
| `curry-leaves` | Launch the full-screen Textual TUI (needs a TTY) |
|
|
48
|
+
| `curry-leaves-repl` | Launch the line REPL (works with piped input) |
|
|
49
|
+
| `python3 examples/01_basic.py "..."` | Run an example end-to-end |
|
|
50
|
+
|
|
51
|
+
To exercise a real turn you need a provider key:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
export ANTHROPIC_API_KEY=sk-ant-... # or OPENAI_API_KEY=sk-...
|
|
55
|
+
curry-leaves
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Or run fully local with [Ollama](https://ollama.com) — no key required:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
ollama pull qwen3
|
|
62
|
+
CURRY_LEAVES_MODEL=qwen3 curry-leaves-repl
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
> **Testing status:** `mypy src` under `strict` is the gate every change must pass, plus `pytest`
|
|
66
|
+
> for the MCP suite. Providers are written so their request builders and stream parsers are plain
|
|
67
|
+
> module functions (not client methods) specifically to be unit-testable — contributions that add
|
|
68
|
+
> a test suite around them are especially welcome.
|
|
69
|
+
|
|
70
|
+
## Project layout
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
src/curry_leaves/
|
|
74
|
+
core/ # messages, events, loop, tools, agent, host, blobs — the kernel
|
|
75
|
+
providers/ # anthropic, openai/ollama, factory, sse, base
|
|
76
|
+
tools/ # read, write, edit, find, search, bash, tasks, ask, web, …
|
|
77
|
+
mcp/ # MCP client: stdio/HTTP servers, manager, settings loader, tool adapter
|
|
78
|
+
session/ # session store + recording
|
|
79
|
+
cli/ # chat REPL + Textual TUI
|
|
80
|
+
util/ # paths, retry, frontmatter, resources
|
|
81
|
+
runner.py prompt.py permission.py thinking.py skills.py compaction.py catalog.py settings.py
|
|
82
|
+
__init__.py # public API surface
|
|
83
|
+
examples/ # runnable examples
|
|
84
|
+
tests/ # pytest suite (MCP subsystem)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The architecture is a strict layering — a **stateless definition** (`Agent`) on a **stateful
|
|
88
|
+
driver** (`Runner`) on a **pure engine** (`agent_loop`) — with all I/O pushed to swappable seams
|
|
89
|
+
(`Provider`, `Host`, `Tool`). Keep that separation in mind: behavior is added at a seam, not by
|
|
90
|
+
branching inside the loop. See the [README architecture section](./README.md#architecture) for the
|
|
91
|
+
big picture.
|
|
92
|
+
|
|
93
|
+
## Pull request workflow
|
|
94
|
+
|
|
95
|
+
1. **Open an issue first** for anything non-trivial, so we can agree on scope and approach.
|
|
96
|
+
2. **Fork and branch** off `main`:
|
|
97
|
+
```bash
|
|
98
|
+
git checkout -b feat/short-description # or fix/… , docs/… , test/…
|
|
99
|
+
```
|
|
100
|
+
3. **Make the change.** Keep the diff focused on one concern.
|
|
101
|
+
4. **Verify it passes:**
|
|
102
|
+
```bash
|
|
103
|
+
mypy src && pytest
|
|
104
|
+
```
|
|
105
|
+
For a change with a runtime surface, also drive the affected flow (a TUI turn, an example, the
|
|
106
|
+
REPL) and confirm it behaves as intended — don't rely on typecheck alone.
|
|
107
|
+
5. **Update docs** (`README.md`, code comments, `examples/`) when behavior or the public API changes.
|
|
108
|
+
6. **Open a pull request** with a clear description of *what* changed and *why*. Link the issue.
|
|
109
|
+
|
|
110
|
+
Keep PRs small and reviewable. A large PR is easier to land when split into focused commits or
|
|
111
|
+
separate PRs.
|
|
112
|
+
|
|
113
|
+
## Code conventions
|
|
114
|
+
|
|
115
|
+
- **`src/curry_leaves/__init__.py` is the public API.** When you add an exported symbol consumers
|
|
116
|
+
should see, re-export it there and add it to `__all__`.
|
|
117
|
+
- **Keep new source under `src/curry_leaves/`** — the package uses a `src/` layout.
|
|
118
|
+
- **Match the surrounding style** — small, single-purpose modules; clear names; comments that explain
|
|
119
|
+
*why*, not *what*. Prefer boring, direct solutions.
|
|
120
|
+
- **Type strictly.** `mypy --strict` is on and must stay green. No new `Any` where a real type fits;
|
|
121
|
+
public functions carry full annotations.
|
|
122
|
+
- **Tools describe their `risk`** (`read` / `write` / `exec` / `network`) — it drives the permission
|
|
123
|
+
fallback, so set it accurately.
|
|
124
|
+
- **Complete the change** — types, docs, and (where practical) a runnable check, not just the happy path.
|
|
125
|
+
|
|
126
|
+
## Extending the kernel
|
|
127
|
+
|
|
128
|
+
Common extension points, each done at a seam rather than by editing the loop:
|
|
129
|
+
|
|
130
|
+
- **Add a tool** — implement the `Tool` protocol (a pydantic args model + `run`), add a factory under
|
|
131
|
+
`src/curry_leaves/tools/`, export it from `__init__.py`, and add it to a preset in `presets.py` if
|
|
132
|
+
it belongs in the default kit. Set `risk` correctly.
|
|
133
|
+
- **Add a provider** — implement `Provider.stream` in `src/curry_leaves/providers/`, keep all
|
|
134
|
+
wire-format translation at that edge (nowhere else), and register it in `providers/factory.py`.
|
|
135
|
+
- **Add a frontend capability** — add a `Request` kind in `src/curry_leaves/core/host.py` (with a
|
|
136
|
+
default value), **not** a new `Host` method — so headless hosts keep working by returning the
|
|
137
|
+
default.
|
|
138
|
+
- **Connect an MCP server** — usually no code change needed: construct `McpServerStdio`/`McpServerHttp`
|
|
139
|
+
(or declare it in `settings.json`'s `mcpServers`) and pick tools with `mcp_tools()`. Framework-level
|
|
140
|
+
MCP changes live under `src/curry_leaves/mcp/` and are covered by `tests/mcp`.
|
|
141
|
+
|
|
142
|
+
## Commit messages
|
|
143
|
+
|
|
144
|
+
Use short, imperative summaries. Conventional-commit prefixes are appreciated but not required:
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
feat: add web_search deferred tool
|
|
148
|
+
fix: retry only on transient provider errors
|
|
149
|
+
docs: clarify Ollama setup
|
|
150
|
+
test: cover the anthropic stream parser
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Reporting bugs
|
|
154
|
+
|
|
155
|
+
Open an issue that includes:
|
|
156
|
+
|
|
157
|
+
- What you did (a minimal code snippet or the exact CLI command).
|
|
158
|
+
- What you expected vs. what happened (include the full error / stack).
|
|
159
|
+
- Your environment: `python --version`, curry-leaves version, provider + model id, OS.
|
|
160
|
+
|
|
161
|
+
A minimal reproduction is the fastest path to a fix.
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
By contributing, you agree that your contributions are licensed under the project's
|
|
166
|
+
[MIT License](./LICENSE).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ilayanambi Ponramu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: curry-leaves
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Curry Leaves — a small, provider-agnostic, multi-agent kernel for building AI agents of any kind: streaming tool-use loop, sub-agents, skills, thinking. The Python port.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ilayanambi-ponramu/curry-leaves-py
|
|
6
|
+
Project-URL: Repository, https://github.com/ilayanambi-ponramu/curry-leaves-py
|
|
7
|
+
Project-URL: Issues, https://github.com/ilayanambi-ponramu/curry-leaves-py/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/ilayanambi-ponramu/curry-leaves-py/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Ilayanambi Ponramu
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agent,ai-agent,anthropic,coding-agent,llm,mcp,multi-agent,ollama,openai,subagents,tool-use
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: httpx>=0.27
|
|
24
|
+
Requires-Dist: mcp>=1.9
|
|
25
|
+
Requires-Dist: pydantic>=2.7
|
|
26
|
+
Requires-Dist: rich>=13.7
|
|
27
|
+
Requires-Dist: textual>=0.58
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.2; extra == 'dev'
|
|
33
|
+
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
<p align="center">
|
|
37
|
+
<img src="assets/logo.png" alt="Curry Leaves logo" width="128" height="128">
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
<h1 align="center">Curry Leaves Agent Loop</h1>
|
|
41
|
+
|
|
42
|
+
<p align="center">A small, provider-agnostic, multi-agent kernel for building AI agents of any kind — in clean, readable Python.</p>
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
<a href="https://pypi.org/project/curry-leaves/"><img src="https://img.shields.io/pypi/v/curry-leaves.svg" alt="PyPI version"></a>
|
|
46
|
+
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license: MIT"></a>
|
|
47
|
+
<a href="https://www.python.org"><img src="https://img.shields.io/badge/python-%3E%3D3.11-brightgreen.svg" alt="python: >=3.11"></a>
|
|
48
|
+
<a href="./src/curry_leaves/py.typed"><img src="https://img.shields.io/badge/types-included-blue.svg" alt="types included"></a>
|
|
49
|
+
<a href="https://github.com/ilayanambi-ponramu/curry-leaves-py"><img src="https://img.shields.io/badge/github-repo-181717.svg?logo=github" alt="GitHub repo"></a>
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
**Curry Leaves** is a general-purpose agent kernel small enough to read in an afternoon. At its core
|
|
53
|
+
is a streaming tool-use loop — call the model, run the tools it asks for, feed the results back,
|
|
54
|
+
repeat — with everything a real agent needs built around it: **sub-agents**, **skills**, **MCP
|
|
55
|
+
tools**, **permission gating**, **session recording**, automatic **context compaction**, and
|
|
56
|
+
per-turn **reasoning-effort sizing**. Point it at any tools and any task; the kernel is
|
|
57
|
+
domain-agnostic — it just happens to ship a batteries-included coding toolset and CLIs on top.
|
|
58
|
+
|
|
59
|
+
One engine, any provider, any UI. The loop knows nothing about a specific LLM wire format — each
|
|
60
|
+
provider translates at its own boundary — so Anthropic, OpenAI, and Ollama are a *config choice, not
|
|
61
|
+
branching logic*. Use it as a **library** (below), or launch either bundled CLI: a full-screen
|
|
62
|
+
terminal UI or a line REPL.
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from curry_leaves import Agent, Runner, coding_tools
|
|
66
|
+
|
|
67
|
+
agent = Agent(model="claude-sonnet-4-5", tools=coding_tools())
|
|
68
|
+
result = await Runner(agent).run("Summarize README.md in three bullets.")
|
|
69
|
+
print(result.output_text)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Table of contents
|
|
75
|
+
|
|
76
|
+
- [Features](#features)
|
|
77
|
+
- [Install](#install)
|
|
78
|
+
- [Quick start](#quick-start)
|
|
79
|
+
- [Configure a provider](#configure-a-provider)
|
|
80
|
+
- [Terminal UI & REPL](#terminal-ui--repl)
|
|
81
|
+
- [Library usage](#library-usage)
|
|
82
|
+
- [Environment variables](#environment-variables)
|
|
83
|
+
- [Architecture](#architecture)
|
|
84
|
+
- [Project layout](#project-layout)
|
|
85
|
+
- [Development](#development)
|
|
86
|
+
- [Contributing](#contributing)
|
|
87
|
+
- [Non-goals](#non-goals)
|
|
88
|
+
- [Acknowledgements](#acknowledgements)
|
|
89
|
+
- [License](#license)
|
|
90
|
+
|
|
91
|
+
## Features
|
|
92
|
+
|
|
93
|
+
- **Multi-provider.** Anthropic, OpenAI, and Ollama (any OpenAI-compatible gateway) implement one
|
|
94
|
+
`Provider.stream()` interface; the loop never changes. Streaming SSE is assembled into a neutral
|
|
95
|
+
`AssistantMessage` at the provider boundary and nowhere else.
|
|
96
|
+
- **Rich toolset.** `read`, `write`, `edit`, `find`, `search`, `bash`,
|
|
97
|
+
`task_create`/`task_update`/`task_list`/`task_get`, `ask`, `current_time`, `web_fetch`,
|
|
98
|
+
`web_search`. Tool args are [pydantic](https://docs.pydantic.dev) models → JSON Schema for free.
|
|
99
|
+
Oversized output is offloaded to an artifact store the model can page through with `read`.
|
|
100
|
+
- **MCP tools.** Connect any [Model Context Protocol](https://modelcontextprotocol.io) server —
|
|
101
|
+
stdio subprocess or HTTP — and pick specific tools by name with `mcp_tools()`; the result is a
|
|
102
|
+
plain `list[Tool]` spliced into `Agent(tools=[...])` like any preset. Servers can also be declared
|
|
103
|
+
in `settings.json`'s `mcpServers` key. MCP tools default to `risk="exec"`, so they always go
|
|
104
|
+
through the permission gate.
|
|
105
|
+
- **Deferred tools + `search_tools`.** Keep the advertised list lean; the model discovers more
|
|
106
|
+
tools by keyword and activates them for the next turn — works across providers.
|
|
107
|
+
- **Sub-agents.** Declare `subagents=[...]`; the parent gets a `task` tool (delegation that returns
|
|
108
|
+
a result) and a `transfer` tool (one-way handoff). Bounded recursion depth.
|
|
109
|
+
- **Structured output.** Give an agent an `output_type` (a pydantic model); the Runner injects the
|
|
110
|
+
schema, validates the final reply, and retries on mismatch. `result.output` is typed.
|
|
111
|
+
- **Auto-thinking.** `auto_thinking=True` sizes reasoning effort (Anthropic thinking budget /
|
|
112
|
+
OpenAI reasoning effort) per turn with a cheap classifier.
|
|
113
|
+
- **Skills.** Drop a `SKILL.md` under `.curry-leaves/skills/<name>/`; its teaser goes into the prompt and
|
|
114
|
+
the model pulls the full body via `read skill://<name>` only when relevant (progressive disclosure).
|
|
115
|
+
- **Permissions.** An opt-in gate authorizes each tool call (`allow` / `ask` / `deny`), with standing
|
|
116
|
+
approvals and contained-change auto-approval. Off by default — headless runs never hang.
|
|
117
|
+
- **Sessions.** Each run can be recorded to `<home>/sessions/<id>/` (`meta.json` + `transcript.jsonl`).
|
|
118
|
+
- **Compaction.** Long conversations are summarized as they near the context window — automatic, or
|
|
119
|
+
on demand via `Runner.compact()` / the `/compact` command.
|
|
120
|
+
- **Typed, async, dependency-light.** Python 3.11+, `mypy --strict`, ships `py.typed`. The library
|
|
121
|
+
core rides on `pydantic` + `httpx`; the CLIs add [Textual](https://textual.textualize.io) + Rich,
|
|
122
|
+
and MCP support uses the official `mcp` SDK.
|
|
123
|
+
|
|
124
|
+
## Install
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
pip install curry-leaves
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Then launch a CLI:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
curry-leaves # full-screen terminal UI (alias: curry)
|
|
134
|
+
curry-leaves-repl # line REPL (works with piped input)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Requires **Python 3.11+**.
|
|
138
|
+
|
|
139
|
+
## Quick start
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
import asyncio
|
|
143
|
+
from curry_leaves import Agent, Runner, coding_tools
|
|
144
|
+
|
|
145
|
+
async def main() -> None:
|
|
146
|
+
agent = Agent(
|
|
147
|
+
model="claude-sonnet-4-5",
|
|
148
|
+
instructions="You are a concise coding assistant.",
|
|
149
|
+
tools=coding_tools(),
|
|
150
|
+
)
|
|
151
|
+
result = await Runner(agent).run("What does src/curry_leaves/runner.py do?")
|
|
152
|
+
print(result.output_text)
|
|
153
|
+
|
|
154
|
+
asyncio.run(main())
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`Agent` is a stateless **definition** (model, tools, instructions); `Runner` holds the live
|
|
158
|
+
conversation and drives the streaming loop. Set an API key first (see below).
|
|
159
|
+
|
|
160
|
+
## Configure a provider
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
export ANTHROPIC_API_KEY=sk-ant-... # or
|
|
164
|
+
export OPENAI_API_KEY=sk-...
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The provider is inferred from the model id (`claude-*` → Anthropic, `gpt-*`/`o1-*` → OpenAI,
|
|
168
|
+
`gemma*`/`llama*`/`qwen*`/… → Ollama), or set `CURRY_LEAVES_PROVIDER` / pass an explicit `provider`
|
|
169
|
+
to the `Agent`.
|
|
170
|
+
|
|
171
|
+
### Local models via Ollama
|
|
172
|
+
|
|
173
|
+
Ollama speaks the OpenAI wire format, so any pulled tag works with no API key:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
ollama pull qwen3
|
|
177
|
+
CURRY_LEAVES_MODEL=qwen3 curry-leaves-repl
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from curry_leaves import Agent, Runner, coding_tools
|
|
182
|
+
agent = Agent(model="qwen3", tools=coding_tools()) # provider → Ollama
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Set `OLLAMA_HOST` to point at a non-default server (`http://host:port`). Tool use needs a model with
|
|
186
|
+
the `tools` capability (qwen3, llama3.x, gemma3, …). Reasoning-effort knobs are dropped for Ollama,
|
|
187
|
+
and usage cost is `$0` (local). You can point the same `OpenAIProvider` at any OpenAI-compatible
|
|
188
|
+
gateway via `OPENAI_BASE_URL` or explicit provider options.
|
|
189
|
+
|
|
190
|
+
## Terminal UI & REPL
|
|
191
|
+
|
|
192
|
+
A full-screen terminal UI (built on [Textual](https://textual.textualize.io)) ships with the
|
|
193
|
+
package — a header bar, a streaming transcript, live thinking blocks, spinner-tracked tool calls,
|
|
194
|
+
indented sub-agent activity, a status bar, and a persistent input box. Finished turns land in real
|
|
195
|
+
terminal scrollback; the active turn streams in place.
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
curry-leaves
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
╭──────────────────────────────────────────────────────────╮
|
|
203
|
+
│ curry-leaves · claude-sonnet-4-5 (anthropic) │
|
|
204
|
+
│ /repo · 11 tools · subagents: explore, plan │
|
|
205
|
+
╰──────────────────────────────────────────────────────────╯
|
|
206
|
+
|
|
207
|
+
you › what does src/curry_leaves/runner.py do?
|
|
208
|
+
ai ›
|
|
209
|
+
→ read({"path":"src/curry_leaves/runner.py"})
|
|
210
|
+
1 """The Runner — holds one live conversation … """
|
|
211
|
+
The Runner composes an Agent with conversation state and drives the loop …
|
|
212
|
+
|
|
213
|
+
● ready in 4213 · out 187 · $0.0155
|
|
214
|
+
╭──────────────────────────────────────────────────────────╮
|
|
215
|
+
│ › ask anything — /help for commands │
|
|
216
|
+
╰──────────────────────────────────────────────────────────╯
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Slash commands:** `/help`, `/reset`, `/tools`, `/skills`, `/model`, `/stats`, `/clear`,
|
|
220
|
+
`/compact [focus]`, `/auto` (toggle contained-change auto-approve), `/autonomous` (toggle self-drive
|
|
221
|
+
mode), `/exit`. The TUI needs an interactive terminal (a TTY).
|
|
222
|
+
|
|
223
|
+
The line-streaming REPL is available as `curry-leaves-repl` — handy for piped / non-TTY input.
|
|
224
|
+
|
|
225
|
+
## Library usage
|
|
226
|
+
|
|
227
|
+
### Stream events instead of awaiting
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
async for event in runner.stream("Refactor the parser"):
|
|
231
|
+
if event.type == "message_update" and event.delta and event.delta.kind == "text":
|
|
232
|
+
print(event.delta.value, end="", flush=True)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Structured output
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
import pydantic
|
|
239
|
+
from curry_leaves import Agent, Runner
|
|
240
|
+
|
|
241
|
+
class Summary(pydantic.BaseModel):
|
|
242
|
+
title: str
|
|
243
|
+
bullets: list[str]
|
|
244
|
+
|
|
245
|
+
agent = Agent(model="claude-sonnet-4-5", output_type=Summary)
|
|
246
|
+
|
|
247
|
+
result = await Runner(agent).run("Summarize this repo.")
|
|
248
|
+
report = result.output # validated Summary instance; the Runner retries on a schema mismatch
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Sub-agents
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
from curry_leaves import Agent, Runner, coding_tools, explore_agent, plan_agent
|
|
255
|
+
|
|
256
|
+
agent = Agent(
|
|
257
|
+
model="claude-sonnet-4-5",
|
|
258
|
+
tools=coding_tools(),
|
|
259
|
+
subagents=[explore_agent("claude-sonnet-4-5"), plan_agent("claude-sonnet-4-5")],
|
|
260
|
+
)
|
|
261
|
+
# The parent can `task` (delegate → result) or `transfer` (one-way handoff) to these.
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### MCP tools
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
from curry_leaves import Agent, Runner, coding_tools
|
|
268
|
+
from curry_leaves.mcp import McpServerStdio, mcp_tools
|
|
269
|
+
|
|
270
|
+
async with McpServerStdio(name="github", command="npx", args=["-y", "@modelcontextprotocol/server-github"]) as gh:
|
|
271
|
+
agent = Agent(
|
|
272
|
+
model="claude-sonnet-4-5",
|
|
273
|
+
tools=[*coding_tools(), *await mcp_tools(gh, "search_issues")],
|
|
274
|
+
)
|
|
275
|
+
result = await Runner(agent).run("Find open issues about streaming.")
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Or declare servers once in `.curry-leaves/settings.json` under `mcpServers` and load them with
|
|
279
|
+
`load_mcp_servers()` + `McpServerManager`. See
|
|
280
|
+
[`examples/07_mcp_tools.py`](examples/07_mcp_tools.py) for a complete, self-contained program.
|
|
281
|
+
|
|
282
|
+
See [`examples/01_basic.py`](examples/01_basic.py) for the smallest runnable program, and
|
|
283
|
+
[`examples/06_host_and_permissions.py`](examples/06_host_and_permissions.py) for a self-contained
|
|
284
|
+
illustration of the host / permission model.
|
|
285
|
+
|
|
286
|
+
## Environment variables
|
|
287
|
+
|
|
288
|
+
| Variable | Purpose | Default |
|
|
289
|
+
|---|---|---|
|
|
290
|
+
| `ANTHROPIC_API_KEY` | Anthropic auth | — |
|
|
291
|
+
| `OPENAI_API_KEY` | OpenAI auth | — |
|
|
292
|
+
| `CURRY_LEAVES_MODEL` | Model id for the CLIs | auto-detected |
|
|
293
|
+
| `CURRY_LEAVES_PROVIDER` | Force a provider (`anthropic` \| `openai` \| `ollama`) | inferred from model id |
|
|
294
|
+
| `CURRY_LEAVES_HOME` | Base dir for settings / skills / sessions | `~/.curry-leaves` |
|
|
295
|
+
| `CURRY_LEAVES_NO_RECORD` | Disable session recording when set | recording on |
|
|
296
|
+
| `OPENAI_BASE_URL` | Point `OpenAIProvider` at any OpenAI-compatible gateway | `https://api.openai.com/v1` |
|
|
297
|
+
| `OLLAMA_HOST` | Ollama server URL | `http://localhost:11434` |
|
|
298
|
+
| `NO_COLOR` | Disable ANSI color | color on |
|
|
299
|
+
|
|
300
|
+
## Architecture
|
|
301
|
+
|
|
302
|
+
The design is a strict layering — a **stateless definition** on a **stateful driver** on a **pure
|
|
303
|
+
engine** — with all I/O pushed to swappable seams (Provider, Host, Tool).
|
|
304
|
+
|
|
305
|
+
| Layer | File | Responsibility |
|
|
306
|
+
|---|---|---|
|
|
307
|
+
| **Message model** | `core/messages.py` | Provider-neutral `Message`/`Content` types — the one thing everything agrees on. |
|
|
308
|
+
| **Events** | `core/events.py` | What the loop yields; a small structural set + a streaming `delta` payload. |
|
|
309
|
+
| **Loop** | `core/loop.py` | The pure engine: `stream → run tools → stream`, while tools are called. Yields events. |
|
|
310
|
+
| **Tools** | `core/tools.py` | A registry of pydantic-typed tools + a concurrent executor with a universal large-result guard. |
|
|
311
|
+
| **Agent** | `core/agent.py` | A stateless definition (model, tools, instructions, sub-agents, `output_type`). |
|
|
312
|
+
| **Runner** | `runner.py` | Live conversation state; builds the `Context` each turn; wires sub-agents, handoff, permissions, compaction. |
|
|
313
|
+
| **Providers** | `providers/*` | The only place that knows a wire format. Anthropic / OpenAI / Ollama behind one `Provider`. |
|
|
314
|
+
| **MCP** | `mcp/*` | MCP client: stdio/HTTP server connections, a manager, a settings loader, and a tool adapter. |
|
|
315
|
+
| **Host** | `core/host.py` | The frontend seam: `emit(event)` + `request(req)`. Headless by default. |
|
|
316
|
+
| **Prompt** | `prompt.py` | Layered system prompt (identity → instructions → env → context → tools), cache-friendly. |
|
|
317
|
+
| **Permission** | `permission.py` | Per-call `allow` / `ask` / `deny` gate with standing approvals. |
|
|
318
|
+
| **Thinking** | `thinking.py` | A tiny classifier that sizes reasoning effort per task. |
|
|
319
|
+
| **Skills** | `skills.py` | Progressive-disclosure capability packages via `skill://` refs. |
|
|
320
|
+
| **Compaction** | `compaction.py` | Summarizes old history as the context window fills. |
|
|
321
|
+
|
|
322
|
+
The one decision that drives the whole loop:
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
runnable = stop_reason in ("tool_use", "stop") AND tool_calls exist
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Tools were called → loop again. None → stop.
|
|
329
|
+
|
|
330
|
+
## Project layout
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
src/curry_leaves/
|
|
334
|
+
core/ # messages, events, loop, tools, agent, host, blobs — the kernel
|
|
335
|
+
providers/ # anthropic, openai/ollama, factory, sse, base
|
|
336
|
+
tools/ # read, write, edit, find, search, bash, tasks, ask, web, …
|
|
337
|
+
mcp/ # MCP client: servers, manager, config, tool adapter
|
|
338
|
+
session/ # session store + recording
|
|
339
|
+
cli/ # chat REPL + Textual TUI
|
|
340
|
+
util/ # paths, retry, frontmatter, resources
|
|
341
|
+
runner.py prompt.py permission.py thinking.py skills.py compaction.py catalog.py settings.py
|
|
342
|
+
__init__.py # public API surface
|
|
343
|
+
examples/ # runnable examples
|
|
344
|
+
tests/ # pytest suite (MCP subsystem)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Development
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
git clone https://github.com/ilayanambi-ponramu/curry-leaves-py.git
|
|
351
|
+
cd curry-leaves-py
|
|
352
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
353
|
+
pip install -e ".[dev]"
|
|
354
|
+
|
|
355
|
+
mypy src # strict type check (the correctness gate)
|
|
356
|
+
pytest # run the test suite
|
|
357
|
+
curry-leaves # launch the Textual TUI (needs a TTY)
|
|
358
|
+
curry-leaves-repl # launch the REPL (works with piped input)
|
|
359
|
+
python3 examples/01_basic.py "What is this project?"
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
`mypy --strict` is the gate every change must pass; `pytest` covers the MCP subsystem.
|
|
363
|
+
Contributions extending the test suite are welcome.
|
|
364
|
+
|
|
365
|
+
## Contributing
|
|
366
|
+
|
|
367
|
+
Contributions are very welcome — bug reports, features, docs, and tests. In short:
|
|
368
|
+
|
|
369
|
+
1. **Open an issue first** for anything non-trivial, so we can agree on the approach.
|
|
370
|
+
2. **Fork & branch** off `main` (`git checkout -b feat/short-description`).
|
|
371
|
+
3. **Make the change**, keep the diff focused, and ensure `mypy src` and `pytest` pass.
|
|
372
|
+
4. **Open a pull request** describing the what and why.
|
|
373
|
+
|
|
374
|
+
See **[CONTRIBUTING.md](./CONTRIBUTING.md)** for the full guide — dev setup, code conventions, how to
|
|
375
|
+
add a tool / provider / frontend capability, commit style, and bug-reporting.
|
|
376
|
+
|
|
377
|
+
## Non-goals
|
|
378
|
+
|
|
379
|
+
curry-leaves is deliberately small. It does **not** include LSP integration, vector stores, or a
|
|
380
|
+
plugin marketplace. If you need those, they belong in a layer built on top of the kernel, not inside
|
|
381
|
+
it. (Unlike the TypeScript sibling, MCP *is* included here — as a thin client layer that feeds the
|
|
382
|
+
existing `Tool` seam, not a change to the kernel.)
|
|
383
|
+
|
|
384
|
+
## Acknowledgements
|
|
385
|
+
|
|
386
|
+
curry-leaves for Python is the sibling of
|
|
387
|
+
[curry-leaves-ts](https://github.com/ilayanambi-ponramu/curry-leaves-ts), a TypeScript kernel of the
|
|
388
|
+
same design; the two ports mirror each other module-for-module.
|
|
389
|
+
|
|
390
|
+
## License
|
|
391
|
+
|
|
392
|
+
[MIT](./LICENSE) © Ilayanambi Ponramu
|