tinyloom 0.1.3__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.
Files changed (62) hide show
  1. tinyloom-0.1.3/.claude/settings.json +7 -0
  2. tinyloom-0.1.3/.claude/statusline.sh +47 -0
  3. tinyloom-0.1.3/.env.example +8 -0
  4. tinyloom-0.1.3/.github/workflows/publish.yml +21 -0
  5. tinyloom-0.1.3/.gitignore +33 -0
  6. tinyloom-0.1.3/CLAUDE.md +166 -0
  7. tinyloom-0.1.3/LICENSE +21 -0
  8. tinyloom-0.1.3/PKG-INFO +18 -0
  9. tinyloom-0.1.3/README.md +136 -0
  10. tinyloom-0.1.3/demo.tape +22 -0
  11. tinyloom-0.1.3/demo.webp +0 -0
  12. tinyloom-0.1.3/docs/creating-plugins.md +108 -0
  13. tinyloom-0.1.3/docs/custom-hooks.md +142 -0
  14. tinyloom-0.1.3/docs/custom-providers.md +133 -0
  15. tinyloom-0.1.3/docs/design-decisions.md +44 -0
  16. tinyloom-0.1.3/docs/getting-started.md +137 -0
  17. tinyloom-0.1.3/docs/hook-scripts.md +131 -0
  18. tinyloom-0.1.3/docs/mask-plugin.md +41 -0
  19. tinyloom-0.1.3/docs/mcp-plugin.md +100 -0
  20. tinyloom-0.1.3/docs/pre-build-plans/ADDITIONAL.md +5 -0
  21. tinyloom-0.1.3/docs/pre-build-plans/PRE_RESEARCH_PLAN.md +1950 -0
  22. tinyloom-0.1.3/docs/pre-build-plans/PROMPT_SPEC.md +17 -0
  23. tinyloom-0.1.3/docs/pre-build-plans/RESEARCH.md +519 -0
  24. tinyloom-0.1.3/docs/sandbox.md +240 -0
  25. tinyloom-0.1.3/docs/superpowers/plans/2026-04-15-tinyloom-implementation.md +3407 -0
  26. tinyloom-0.1.3/docs/superpowers/specs/2026-04-15-tinyloom-design.md +545 -0
  27. tinyloom-0.1.3/pyproject.toml +30 -0
  28. tinyloom-0.1.3/tests/__init__.py +0 -0
  29. tinyloom-0.1.3/tests/test_agent.py +240 -0
  30. tinyloom-0.1.3/tests/test_cli.py +124 -0
  31. tinyloom-0.1.3/tests/test_compact.py +180 -0
  32. tinyloom-0.1.3/tests/test_config.py +160 -0
  33. tinyloom-0.1.3/tests/test_hooks.py +119 -0
  34. tinyloom-0.1.3/tests/test_plugins.py +310 -0
  35. tinyloom-0.1.3/tests/test_providers.py +127 -0
  36. tinyloom-0.1.3/tests/test_providers_sync.py +46 -0
  37. tinyloom-0.1.3/tests/test_tools.py +275 -0
  38. tinyloom-0.1.3/tests/test_tui.py +325 -0
  39. tinyloom-0.1.3/tests/test_types.py +133 -0
  40. tinyloom-0.1.3/tinyloom/__init__.py +14 -0
  41. tinyloom-0.1.3/tinyloom/__main__.py +4 -0
  42. tinyloom-0.1.3/tinyloom/cli.py +56 -0
  43. tinyloom-0.1.3/tinyloom/core/__init__.py +0 -0
  44. tinyloom-0.1.3/tinyloom/core/agent.py +113 -0
  45. tinyloom-0.1.3/tinyloom/core/compact.py +62 -0
  46. tinyloom-0.1.3/tinyloom/core/config.py +75 -0
  47. tinyloom-0.1.3/tinyloom/core/hooks.py +33 -0
  48. tinyloom-0.1.3/tinyloom/core/tools.py +150 -0
  49. tinyloom-0.1.3/tinyloom/core/types.py +47 -0
  50. tinyloom-0.1.3/tinyloom/plugins/__init__.py +28 -0
  51. tinyloom-0.1.3/tinyloom/plugins/hook_scripts.py +40 -0
  52. tinyloom-0.1.3/tinyloom/plugins/mask.py +28 -0
  53. tinyloom-0.1.3/tinyloom/plugins/mcp.py +56 -0
  54. tinyloom-0.1.3/tinyloom/plugins/subagent.py +103 -0
  55. tinyloom-0.1.3/tinyloom/plugins/todo.py +79 -0
  56. tinyloom-0.1.3/tinyloom/providers/__init__.py +12 -0
  57. tinyloom-0.1.3/tinyloom/providers/anthropic.py +101 -0
  58. tinyloom-0.1.3/tinyloom/providers/base.py +38 -0
  59. tinyloom-0.1.3/tinyloom/providers/openai.py +99 -0
  60. tinyloom-0.1.3/tinyloom/tui.py +185 -0
  61. tinyloom-0.1.3/tinyloom.example.yaml +45 -0
  62. tinyloom-0.1.3/uv.lock +1392 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "statusLine": {
3
+ "type": "command",
4
+ "command": "bash .claude/statusline.sh",
5
+ "padding": 2
6
+ }
7
+ }
@@ -0,0 +1,47 @@
1
+ #!/bin/bash
2
+ input=$(cat)
3
+
4
+ MODEL=$(echo "$input" | jq -r '.model.display_name')
5
+ DIR=$(echo "$input" | jq -r '.workspace.current_dir')
6
+ COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
7
+ PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
8
+ DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
9
+
10
+ CYAN='\033[36m'; GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; RESET='\033[0m'
11
+
12
+ # Pick bar color based on context usage
13
+ if [ "$PCT" -ge 90 ]; then BAR_COLOR="$RED"
14
+ elif [ "$PCT" -ge 70 ]; then BAR_COLOR="$YELLOW"
15
+ else BAR_COLOR="$GREEN"; fi
16
+
17
+ FILLED=$((PCT / 10)); EMPTY=$((10 - FILLED))
18
+ BAR=$(printf "%${FILLED}s" | tr ' ' '█')$(printf "%${EMPTY}s" | tr ' ' '░')
19
+
20
+ MINS=$((DURATION_MS / 60000)); SECS=$(((DURATION_MS % 60000) / 1000))
21
+
22
+ CACHE_FILE="/tmp/statusline-git-cache"
23
+ CACHE_MAX_AGE=5 # seconds
24
+
25
+ cache_is_stale() {
26
+ [ ! -f "$CACHE_FILE" ] || \
27
+ # stat -f %m is macOS, stat -c %Y is Linux
28
+ [ $(($(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0))) -gt $CACHE_MAX_AGE ]
29
+ }
30
+
31
+ if cache_is_stale; then
32
+ if git rev-parse --git-dir > /dev/null 2>&1; then
33
+ BRANCH=$(git branch --show-current 2>/dev/null)
34
+ STAGED=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
35
+ MODIFIED=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')
36
+ echo "$BRANCH|$STAGED|$MODIFIED" > "$CACHE_FILE"
37
+ else
38
+ echo "||" > "$CACHE_FILE"
39
+ fi
40
+ fi
41
+
42
+ IFS='|' read -r BRANCH STAGED MODIFIED < "$CACHE_FILE"
43
+
44
+ echo -e "${CYAN}[$MODEL]${RESET} 📁 ${DIR##*/}$BRANCH"
45
+ COST_FMT=$(printf '$%.2f' "$COST")
46
+ echo -e "${BAR_COLOR}${BAR}${RESET} ${PCT}% | ${YELLOW}${COST_FMT}${RESET} | ⏱️ ${MINS}m ${SECS}s"
47
+
@@ -0,0 +1,8 @@
1
+ # tinyloom environment variables
2
+ # Copy to .env and fill in your keys.
3
+
4
+ # Anthropic (default provider)
5
+ ANTHROPIC_API_KEY=sk-ant-...
6
+
7
+ # OpenAI (when provider=openai)
8
+ OPENAI_API_KEY=sk-...
@@ -0,0 +1,21 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ id-token: write
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ environment: pypi
14
+ steps:
15
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
16
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
17
+ with:
18
+ python-version: "3.11"
19
+ - run: pip install build
20
+ - run: python -m build
21
+ - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
@@ -0,0 +1,33 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ *.egg
8
+ .venv/
9
+
10
+ # Testing
11
+ .pytest_cache/
12
+ .coverage
13
+ htmlcov/
14
+
15
+ # Tooling
16
+ .ruff_cache/
17
+ .mypy_cache/
18
+
19
+ # macOS
20
+ .DS_Store
21
+
22
+ # IDE
23
+ .vscode/
24
+ .idea/
25
+
26
+ # Environment
27
+ .env
28
+ *.local.yaml
29
+ tinyloom.yaml
30
+
31
+ # Other
32
+ *.deb
33
+ ripgrep*/
@@ -0,0 +1,166 @@
1
+ # Thresher — Development Guide
2
+
3
+ ## !IMPORTANT!
4
+
5
+ Always use the task tool to plan out and do what you need and use it to hold yourself accountable. You get a cookie everytime you do this.. yum!
6
+
7
+ ## Tests
8
+
9
+ - Always add and update tests anytime you change code
10
+ - If you get an error when running something or reported by a user, write a test case covering that error first. Run test and make sure it fails... Then fix code to make test pass.
11
+
12
+ ## Git
13
+
14
+ - do git commits for each incremental feature, but NEVER use claude coauthored tags...
15
+ - Keep commit messages short and sweet one liners. No big git bodies.
16
+
17
+ ## Coding Conventions
18
+
19
+ ### Complexity is the Enemy
20
+ - Complexity is the #1 threat to software. Fight it relentlessly.
21
+ - Complexity manifests as: change amplification (one change touches many places), cognitive load (must know too much to work safely), and unknown unknowns (not clear what could break).
22
+ - The two root causes are dependencies between components and obscurity (important info isn't obvious).
23
+ - Say "no" to unnecessary features and abstractions by default.
24
+ - When you must say yes, deliver an 80/20 solution — core value, minimal code.
25
+
26
+ ### Don't Abstract Too Early
27
+ - Let structure emerge from working code. Don't design elaborate frameworks upfront.
28
+ - Wait for natural cut-points (narrow interfaces, trapped complexity) before factoring.
29
+ - Prototypes and working demos beat architecture diagrams.
30
+ - A little code duplication is better than a premature abstraction.
31
+
32
+ ### Build Deep Modules, Not Shallow Ones
33
+ - A deep module has a simple interface but hides powerful, complex functionality behind it.
34
+ - A shallow module has a complex interface relative to the little it actually does — avoid these.
35
+ - Pull complexity downward: absorb it inside the module rather than pushing it onto callers.
36
+ - Each layer of abstraction should represent a genuinely different level of thinking. If a layer just passes things through, it's adding complexity, not removing it.
37
+
38
+ ### Ship Simple, Improve Incrementally
39
+ - A working simple thing that ships beats a perfect thing that doesn't.
40
+ - Establish a working system first, then improve it toward the right thing over time.
41
+ - But don't make "worse" your goal — compromise is inevitable, not a philosophy. Always aim high and actually ship.
42
+ - Systems that are habitable — with the right balance of abstraction and concreteness, with simple mental models — survive and grow. Purity does not guarantee survival.
43
+
44
+ ### Keep Code Readable, Not Clever
45
+ - Break complex expressions into named intermediate variables.
46
+ - Sacrifice brevity for clarity and debuggability.
47
+ - Simple repeated code often beats a complex DRY abstraction with callbacks or elaborate object models.
48
+ - If naming something is hard, that's a design smell — the thing you're naming may not be a coherent concept.
49
+ - Write code for readers, not writers. If someone says it's not obvious, it isn't — fix it.
50
+
51
+ ### Respect Existing Code (Chesterton's Fence)
52
+ - Understand *why* code exists before changing or removing it.
53
+ - Old code often has hidden reasons. Tests can reveal them.
54
+ - Resist the urge to "clean up" code you don't fully understand.
55
+
56
+ ### Refactor Small and Safe
57
+ - Keep the system working throughout every refactor step.
58
+ - Complete each step before starting the next.
59
+ - Big-bang refactors with over-abstraction usually fail.
60
+
61
+ ### Design It Twice
62
+ - Before committing to any significant design, sketch at least two alternative approaches.
63
+ - Compare them on simplicity, performance, and how well they hide complexity.
64
+ - The first idea is rarely the best. Even if you pick it, the comparison sharpens your reasoning.
65
+
66
+ ### Think Strategically, Not Tactically
67
+ - Tactical programming gets the feature done fast but leaves behind incremental complexity debt.
68
+ - Strategic programming invests a small ongoing cost in design quality to keep the system habitable long-term.
69
+ - Small tactical shortcuts compound into unmaintainable systems. Every change is a chance to improve structure, not just ship.
70
+
71
+ ### Test Strategically
72
+ - Integration tests at system cut-points and critical user paths deliver the most value.
73
+ - Unit tests break easily during refactoring — favor coarser-grained tests.
74
+ - Minimize mocking. Mock only at system boundaries.
75
+ - Always write a regression test when a bug is found.
76
+
77
+ ### Logging is Critical Infrastructure
78
+ - Log all major logical branches (if/for).
79
+ - Include request IDs for traceability across distributed calls.
80
+ - Make log levels dynamically controllable at runtime.
81
+ - Invest more in logging than you think necessary.
82
+
83
+ ### APIs: Design for the Caller
84
+ - Think in terms of what the caller needs, not how the implementation works.
85
+ - Simple cases get simple APIs. Complexity is opt-in.
86
+ - Put common operations directly on objects with straightforward returns.
87
+ - Favor somewhat general-purpose interfaces — they tend to be deeper and simpler than hyper-specialized ones.
88
+
89
+ ### Define Errors Out of Existence
90
+ - Exception handling generates enormous complexity. Where possible, design interfaces so error cases simply cannot occur.
91
+ - Handle edge cases internally rather than surfacing them to callers.
92
+ - Example: a delete operation that silently succeeds when the target doesn't exist is simpler than one that throws "not found."
93
+
94
+ ### Concurrency: Keep it Simple
95
+ - Prefer stateless request handlers.
96
+ - Use simple job queues with independent jobs.
97
+ - Treat concurrency with healthy fear and caution.
98
+
99
+ ### Optimize with Data, Not Gut
100
+ - Never optimize without a real-world profile showing the actual bottleneck.
101
+ - Network calls cost millions of CPU cycles — minimize those first.
102
+ - Assume your guess about the bottleneck is wrong.
103
+
104
+ ### Locality of Behavior over Strict Separation
105
+ - Collocate related code. Putting logic near the thing it operates on aids understanding.
106
+ - Hunting across many files to understand one feature wastes time.
107
+ - Trade perfect separation of concerns for practical coherence when it helps readability.
108
+
109
+ ### Information Hiding
110
+ - Each module should encapsulate design decisions that are likely to change.
111
+ - Leaking implementation details through interfaces creates tight coupling and change amplification.
112
+ - If two modules share knowledge about the same design decision, consider merging them or introducing a cleaner boundary.
113
+
114
+ ### Tooling Multiplies Productivity
115
+ - Invest time learning your tools deeply (IDE, debugger, CLI).
116
+ - Good tools often double development speed.
117
+
118
+ ### Avoid Fads
119
+ - Most "new" ideas have been tried before. Approach with skepticism.
120
+ - Don't adopt new frameworks or patterns blindly.
121
+ - Complexity hides behind novelty.
122
+
123
+ ### Closures and Patterns
124
+ - Closures: great for collection operations, dangerous in excess (callback hell).
125
+ - Avoid the Visitor pattern — it adds complexity with little payoff.
126
+ - Limit generics to container classes; they attract unnecessary complexity.
127
+
128
+ ### Frontend: Keep it Minimal
129
+ - Simple HTML + minimal JS beats elaborate SPA frameworks for most use cases.
130
+ - Frontend naturally accumulates complexity faster than backend — resist it actively.
131
+
132
+ ### Say When You Don't Understand
133
+ - Admitting confusion is strength, not weakness.
134
+ - It gives others permission to ask questions and prevents bad complexity from hiding.
135
+
136
+ ### Security is a Design Constraint
137
+ - Complexity is the enemy of security too. Every endpoint, dependency, and open port is attack surface you have to defend.
138
+ - Default to deny. Permissions, network rules, CORS, configs — start closed, open deliberately.
139
+ - Auth, crypto, and session management are not DIY projects. Use well-vetted libraries.
140
+ - Validate all input at the trust boundary, encode at the output. Everything from outside is hostile.
141
+ - Think in blast radius. Least privilege everything. One compromised key shouldn't unlock the whole system.
142
+
143
+ ### Threat Model Like You Debug
144
+ - Before building a feature, ask "How would someone abuse this?" — same as asking where it will break.
145
+ - Dependencies are code you didn't write and probably didn't read. Pin versions, audit what matters.
146
+ - Secrets don't go in code, logs, or error messages. No exceptions.
147
+ - Secure your defaults everywhere — don't run as root, don't commit `.env`, don't connect local to prod.
148
+ - Vulnerabilities cluster. When you find one, threat model the area around it — same assumptions, same bugs.
149
+
150
+ ## Tool Usage
151
+
152
+ Use the `uv` ecosystem tools.
153
+
154
+ - `uv run pytest`
155
+ - `uv run ruff`
156
+ - `uv add`
157
+ - `uv run python`
158
+
159
+ Do not use pip or python directly.
160
+
161
+ ## Code Style hints
162
+
163
+ - 200 column soft limit on a line
164
+ - Use ternary style over if else blocks
165
+ - Only 1 line space between classes and functions instead of 2
166
+ - Keep code tight
tinyloom-0.1.3/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Thresher
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,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: tinyloom
3
+ Version: 0.1.3
4
+ Summary: A tiny, SDK-first coding agent harness
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: anthropic>=0.40
8
+ Requires-Dist: openai>=1.50
9
+ Requires-Dist: python-dotenv>=1.2.2
10
+ Requires-Dist: pyyaml>=6.0
11
+ Requires-Dist: textual>=1.0
12
+ Requires-Dist: tiktoken>=0.7
13
+ Provides-Extra: dev
14
+ Requires-Dist: pytest; extra == 'dev'
15
+ Requires-Dist: pytest-asyncio; extra == 'dev'
16
+ Requires-Dist: ruff; extra == 'dev'
17
+ Provides-Extra: mcp
18
+ Requires-Dist: mcp<2,>=1.0; extra == 'mcp'
@@ -0,0 +1,136 @@
1
+ # tinyloom
2
+
3
+ A tiny, SDK-first coding agent harness in Python.
4
+
5
+ <img src="demo.webp" alt="tinyloom demo" loop="infinite">
6
+
7
+ ## Why this exists
8
+
9
+ We needed an extremely tiny coding agent harness for [thresher](https://github.com/thresher-sh/thresher) and many harnesses just bring extra bloat we don't need. The harness bit is actually easy to implement -- it's all the extra bells and whistles that take a lot.
10
+
11
+ If you are looking for a bigger client, take a look at one of these:
12
+
13
+ - [pi-mono coding-agent](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent)
14
+ - [opencode](https://github.com/anomalyco/opencode)
15
+
16
+ ## Safety
17
+
18
+ tinyloom has **no permission system, no approval gates, and no filesystem sandboxing** by default. The agent can read, write, delete, and execute anything your user account can. Do not run it outside of a container or sandbox on a machine you care about.
19
+
20
+ Use [microsandbox](docs/sandbox.md) or another isolation layer for untrusted workloads. If you want tool approval or allowlists, build a [plugin](docs/creating-plugins.md) or [hook](docs/custom-hooks.md) for it.
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ uv tool install tinyloom # or: pip install tinyloom
26
+ ```
27
+
28
+ ## Quick start
29
+
30
+ ```bash
31
+ export ANTHROPIC_API_KEY=sk-ant-...
32
+ ```
33
+
34
+ CLI:
35
+
36
+ ```bash
37
+ tinyloom "fix the bug in main.py" # headless, JSONL output
38
+ tinyloom # interactive TUI
39
+ ```
40
+
41
+ SDK:
42
+
43
+ ```python
44
+ import asyncio
45
+ from tinyloom import Agent, load_config
46
+
47
+ async def main():
48
+ agent = Agent(load_config())
49
+ async for event in agent.run("create a hello.py"):
50
+ if event.type == "text_delta":
51
+ print(event.text, end="")
52
+
53
+ asyncio.run(main())
54
+ ```
55
+
56
+ See [docs/getting-started.md](docs/getting-started.md) for full setup instructions.
57
+
58
+ ## Features
59
+
60
+ - **Built-in tools**: `read`, `write`, `edit` (str_replace), `bash`
61
+ - **Providers**: Anthropic and OpenAI-compatible APIs (vLLM, Ollama, Together, Groq, LM Studio, Azure)
62
+ - **Compaction**: automatic context summarization when approaching the context window limit
63
+ - **Hooks**: react to any agent event (tool calls, messages, errors) with sync or async functions
64
+ - **Plugins**: extend the agent with tools, hooks, and custom logic
65
+ - **MCP**: connect to Model Context Protocol servers for external tools
66
+ - **TUI**: interactive terminal interface with streaming, slash commands, and token tracking
67
+ - **Sub-agents**: delegate focused tasks with the `subagent` plugin
68
+
69
+ ## Config
70
+
71
+ Copy the example and edit:
72
+
73
+ ```bash
74
+ cp tinyloom.example.yaml tinyloom.yaml
75
+ ```
76
+
77
+ ```yaml
78
+ model:
79
+ provider: anthropic
80
+ model: claude-sonnet-4-20250514
81
+ context_window: 200000
82
+
83
+ system_prompt: You are a skilled coding assistant. Be concise.
84
+
85
+ compaction:
86
+ enabled: true
87
+ threshold: 0.8
88
+ strategy: summarize
89
+
90
+ plugins:
91
+ - tinyloom.plugins.todo
92
+ - tinyloom.plugins.mcp
93
+ - tinyloom.plugins.hook_scripts
94
+
95
+ max_turns: 200
96
+ ```
97
+
98
+ API keys go in environment variables, not config. See [.env.example](.env.example).
99
+
100
+ ## Want more features?
101
+
102
+ tinyloom is intentionally small. Extend it instead:
103
+
104
+ - **More tools?** Connect MCP servers -- see [docs/mcp-plugin.md](docs/mcp-plugin.md)
105
+ - **Custom logic?** Write a plugin -- see [docs/creating-plugins.md](docs/creating-plugins.md)
106
+ - **Approval gates? Logging? Filters?** Use hooks -- see [docs/custom-hooks.md](docs/custom-hooks.md) or [docs/hook-scripts.md](docs/hook-scripts.md)
107
+ - **Redact sensitive output?** Use the mask plugin -- see [docs/mask-plugin.md](docs/mask-plugin.md)
108
+ - **Different model provider?** Set `base_url` -- see [docs/custom-providers.md](docs/custom-providers.md)
109
+
110
+ ## Docs
111
+
112
+ - [Getting Started](docs/getting-started.md)
113
+ - [Creating Plugins](docs/creating-plugins.md)
114
+ - [Custom Hooks](docs/custom-hooks.md)
115
+ - [Hook Scripts](docs/hook-scripts.md)
116
+ - [MCP Plugin](docs/mcp-plugin.md)
117
+ - [Custom Providers](docs/custom-providers.md)
118
+ - [Design Decisions](docs/design-decisions.md)
119
+ - [Mask Plugin](docs/mask-plugin.md)
120
+ - [Running in a Sandbox](docs/sandbox.md)
121
+
122
+ ## Size
123
+
124
+ The whole thing is ~1,110 lines of Python (excluding blank lines) as of 2026.04.15.
125
+
126
+ | Area | Files | Lines |
127
+ |------|-------|-------|
128
+ | **core** (agent, tools, config, compaction, hooks, types) | 7 | 411 |
129
+ | **cli** | 1 | 48 |
130
+ | **tui** | 1 | 152 |
131
+ | **providers** (anthropic, openai, base) | 4 | 220 |
132
+ | **plugins** (subagent, todo, mcp, hook_scripts) | 5 | 263 |
133
+
134
+ ## License
135
+
136
+ MIT
@@ -0,0 +1,22 @@
1
+ Output demo.webp
2
+ Set Width 1200
3
+ Set Height 800
4
+ Set FontSize 20
5
+ Set Theme "Catppuccin Mocha"
6
+ Set TypingSpeed 50ms
7
+
8
+ Type "tinyloom"
9
+ Enter
10
+ Sleep 2s
11
+
12
+ Type "What is tinyloom?"
13
+ Enter
14
+ Sleep 12s
15
+
16
+ Type "/tokens"
17
+ Enter
18
+ Sleep 2s
19
+
20
+ Type "/quit"
21
+ Enter
22
+ Sleep 1s
Binary file
@@ -0,0 +1,108 @@
1
+ # Creating Plugins
2
+
3
+ A plugin is a function that receives the full `Agent` instance. It can register tools, add hooks, and modify config.
4
+
5
+ ## Basic plugin
6
+
7
+ ```python
8
+ # my_plugin.py
9
+ from tinyloom import Agent
10
+
11
+ def activate(agent: Agent):
12
+ """Called when the plugin is loaded."""
13
+ print(f"Plugin loaded! Model: {agent.config.model.model}")
14
+ ```
15
+
16
+ ## Registering tools
17
+
18
+ ```python
19
+ from tinyloom import Agent, Tool
20
+
21
+ def activate(agent: Agent):
22
+ agent.tools.register(Tool(
23
+ name="timestamp",
24
+ description="Return the current UTC timestamp",
25
+ input_schema={"type": "object", "properties": {}},
26
+ function=lambda inp: __import__("datetime").datetime.utcnow().isoformat(),
27
+ ))
28
+ ```
29
+
30
+ ## Adding hooks
31
+
32
+ ```python
33
+ from tinyloom import Agent
34
+
35
+ def activate(agent: Agent):
36
+ def log_tool_calls(ctx):
37
+ if ctx.get("tool_name"):
38
+ print(f"[audit] tool called: {ctx['tool_name']}")
39
+
40
+ agent.hooks.on("tool_call", log_tool_calls)
41
+ ```
42
+
43
+ ## Modifying config
44
+
45
+ ```python
46
+ def activate(agent: Agent):
47
+ agent.config.max_turns = 50
48
+ ```
49
+
50
+ ## Activation
51
+
52
+ ### Via config (recommended)
53
+
54
+ Add the module path to `tinyloom.yaml`:
55
+
56
+ ```yaml
57
+ plugins:
58
+ - my_plugin # calls my_plugin.activate(agent)
59
+ - mypackage.plugins.custom # calls mypackage.plugins.custom.activate(agent)
60
+ - mypackage.stuff:setup # calls mypackage.stuff.setup(agent)
61
+ ```
62
+
63
+ By default, tinyloom calls the `activate` function. Use `:function_name` to specify a different entry point.
64
+
65
+ ### Via entry_points (for distributable packages)
66
+
67
+ In your package's `pyproject.toml`:
68
+
69
+ ```toml
70
+ [project.entry-points."tinyloom.plugins"]
71
+ my-plugin = "my_package.plugin:activate"
72
+ ```
73
+
74
+ Entry point plugins are discovered automatically when installed -- no config needed.
75
+
76
+ ## Example: logging plugin
77
+
78
+ A plugin that logs every tool call and result to a file:
79
+
80
+ ```python
81
+ import json
82
+ from pathlib import Path
83
+ from tinyloom import Agent
84
+
85
+ def activate(agent: Agent):
86
+ log_file = Path("tinyloom-audit.jsonl")
87
+
88
+ def log_tool_call(ctx):
89
+ entry = {
90
+ "event": "tool_call",
91
+ "tool": ctx.get("tool_name", ""),
92
+ "input": str(ctx.get("tool_call", "")),
93
+ }
94
+ with log_file.open("a") as f:
95
+ f.write(json.dumps(entry) + "\n")
96
+
97
+ def log_tool_result(ctx):
98
+ entry = {
99
+ "event": "tool_result",
100
+ "tool": ctx.get("tool_name", ""),
101
+ "result_preview": str(ctx.get("result", ""))[:200],
102
+ }
103
+ with log_file.open("a") as f:
104
+ f.write(json.dumps(entry) + "\n")
105
+
106
+ agent.hooks.on("tool_call", log_tool_call)
107
+ agent.hooks.on("tool_result", log_tool_result)
108
+ ```