the-machine 0.1.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.
Files changed (80) hide show
  1. the_machine-0.1.0/.github/workflows/ci.yml +63 -0
  2. the_machine-0.1.0/.github/workflows/publish.yml +64 -0
  3. the_machine-0.1.0/.gitignore +25 -0
  4. the_machine-0.1.0/LICENSE +21 -0
  5. the_machine-0.1.0/PKG-INFO +371 -0
  6. the_machine-0.1.0/README.md +334 -0
  7. the_machine-0.1.0/README.zh-CN.md +322 -0
  8. the_machine-0.1.0/RELEASE.md +93 -0
  9. the_machine-0.1.0/pyproject.toml +85 -0
  10. the_machine-0.1.0/scripts/smoke_deepseek.py +106 -0
  11. the_machine-0.1.0/src/tm/__init__.py +3 -0
  12. the_machine-0.1.0/src/tm/ai/__init__.py +29 -0
  13. the_machine-0.1.0/src/tm/ai/catalog.py +193 -0
  14. the_machine-0.1.0/src/tm/ai/event_stream.py +67 -0
  15. the_machine-0.1.0/src/tm/ai/providers/__init__.py +13 -0
  16. the_machine-0.1.0/src/tm/ai/providers/anthropic.py +289 -0
  17. the_machine-0.1.0/src/tm/ai/providers/base.py +51 -0
  18. the_machine-0.1.0/src/tm/ai/providers/google.py +253 -0
  19. the_machine-0.1.0/src/tm/ai/providers/openai_compat.py +303 -0
  20. the_machine-0.1.0/src/tm/ai/registry.py +161 -0
  21. the_machine-0.1.0/src/tm/ai/types.py +200 -0
  22. the_machine-0.1.0/src/tm/cli/__init__.py +0 -0
  23. the_machine-0.1.0/src/tm/cli/commands.py +326 -0
  24. the_machine-0.1.0/src/tm/cli/console_ui.py +84 -0
  25. the_machine-0.1.0/src/tm/cli/main.py +557 -0
  26. the_machine-0.1.0/src/tm/config.py +106 -0
  27. the_machine-0.1.0/src/tm/context/__init__.py +3 -0
  28. the_machine-0.1.0/src/tm/context/agents_md.py +58 -0
  29. the_machine-0.1.0/src/tm/core/__init__.py +20 -0
  30. the_machine-0.1.0/src/tm/core/agent.py +330 -0
  31. the_machine-0.1.0/src/tm/core/compaction.py +80 -0
  32. the_machine-0.1.0/src/tm/core/events.py +84 -0
  33. the_machine-0.1.0/src/tm/core/loop.py +117 -0
  34. the_machine-0.1.0/src/tm/core/session.py +244 -0
  35. the_machine-0.1.0/src/tm/core/system_prompt.py +22 -0
  36. the_machine-0.1.0/src/tm/extensions.py +86 -0
  37. the_machine-0.1.0/src/tm/permissions/__init__.py +36 -0
  38. the_machine-0.1.0/src/tm/permissions/actions.py +36 -0
  39. the_machine-0.1.0/src/tm/permissions/approval.py +60 -0
  40. the_machine-0.1.0/src/tm/permissions/audit.py +43 -0
  41. the_machine-0.1.0/src/tm/permissions/gate.py +82 -0
  42. the_machine-0.1.0/src/tm/permissions/policy.py +134 -0
  43. the_machine-0.1.0/src/tm/prompts.py +58 -0
  44. the_machine-0.1.0/src/tm/py.typed +0 -0
  45. the_machine-0.1.0/src/tm/skills.py +96 -0
  46. the_machine-0.1.0/src/tm/tools/__init__.py +34 -0
  47. the_machine-0.1.0/src/tm/tools/base.py +78 -0
  48. the_machine-0.1.0/src/tm/tools/edit.py +59 -0
  49. the_machine-0.1.0/src/tm/tools/find.py +84 -0
  50. the_machine-0.1.0/src/tm/tools/grep.py +106 -0
  51. the_machine-0.1.0/src/tm/tools/ls.py +41 -0
  52. the_machine-0.1.0/src/tm/tools/path_utils.py +13 -0
  53. the_machine-0.1.0/src/tm/tools/read.py +50 -0
  54. the_machine-0.1.0/src/tm/tools/registry.py +31 -0
  55. the_machine-0.1.0/src/tm/tools/shell.py +136 -0
  56. the_machine-0.1.0/src/tm/tools/truncate.py +25 -0
  57. the_machine-0.1.0/src/tm/tools/write.py +30 -0
  58. the_machine-0.1.0/src/tm/tui/__init__.py +15 -0
  59. the_machine-0.1.0/src/tm/tui/app.py +241 -0
  60. the_machine-0.1.0/src/tm/utils/__init__.py +5 -0
  61. the_machine-0.1.0/src/tm/utils/abort.py +28 -0
  62. the_machine-0.1.0/tests/test_agent.py +174 -0
  63. the_machine-0.1.0/tests/test_anthropic.py +191 -0
  64. the_machine-0.1.0/tests/test_commands.py +262 -0
  65. the_machine-0.1.0/tests/test_compaction.py +122 -0
  66. the_machine-0.1.0/tests/test_config.py +42 -0
  67. the_machine-0.1.0/tests/test_context.py +42 -0
  68. the_machine-0.1.0/tests/test_event_stream.py +39 -0
  69. the_machine-0.1.0/tests/test_extensions.py +93 -0
  70. the_machine-0.1.0/tests/test_google.py +169 -0
  71. the_machine-0.1.0/tests/test_openai_compat.py +224 -0
  72. the_machine-0.1.0/tests/test_permissions.py +159 -0
  73. the_machine-0.1.0/tests/test_prompts.py +41 -0
  74. the_machine-0.1.0/tests/test_registry.py +85 -0
  75. the_machine-0.1.0/tests/test_session.py +186 -0
  76. the_machine-0.1.0/tests/test_skills.py +62 -0
  77. the_machine-0.1.0/tests/test_smoke_deepseek.py +32 -0
  78. the_machine-0.1.0/tests/test_tools.py +132 -0
  79. the_machine-0.1.0/tests/test_tui.py +165 -0
  80. the_machine-0.1.0/uv.lock +1495 -0
@@ -0,0 +1,63 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ env:
13
+ UV_LINK_MODE: copy
14
+
15
+ jobs:
16
+ lint:
17
+ name: lint (ruff + mypy)
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: astral-sh/setup-uv@v5
22
+ with:
23
+ enable-cache: true
24
+ - name: Install dependencies
25
+ run: uv sync --locked --all-extras
26
+ - name: Ruff
27
+ run: uv run ruff check .
28
+ - name: Mypy
29
+ run: uv run mypy
30
+
31
+ test:
32
+ name: test (${{ matrix.os }}, py${{ matrix.python }})
33
+ runs-on: ${{ matrix.os }}
34
+ strategy:
35
+ fail-fast: false
36
+ matrix:
37
+ os: [ubuntu-latest, windows-latest, macos-latest]
38
+ python: ["3.11", "3.12"]
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+ - uses: astral-sh/setup-uv@v5
42
+ with:
43
+ enable-cache: true
44
+ python-version: ${{ matrix.python }}
45
+ - name: Install dependencies
46
+ run: uv sync --locked --all-extras
47
+ - name: Run tests
48
+ run: uv run pytest -q
49
+
50
+ build:
51
+ name: build
52
+ runs-on: ubuntu-latest
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+ - uses: astral-sh/setup-uv@v5
56
+ with:
57
+ enable-cache: true
58
+ - name: Build wheel and sdist
59
+ run: uv build
60
+ - uses: actions/upload-artifact@v4
61
+ with:
62
+ name: dist
63
+ path: dist/
@@ -0,0 +1,64 @@
1
+ name: Publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ inputs:
8
+ target:
9
+ description: Where to publish
10
+ type: choice
11
+ options: [testpypi, pypi]
12
+ default: testpypi
13
+
14
+ jobs:
15
+ build:
16
+ name: build
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: astral-sh/setup-uv@v5
21
+ - name: Build
22
+ run: uv build
23
+ - name: Check metadata
24
+ run: uvx twine check dist/*
25
+ - uses: actions/upload-artifact@v4
26
+ with:
27
+ name: dist
28
+ path: dist/
29
+
30
+ testpypi:
31
+ name: publish to TestPyPI
32
+ if: github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'testpypi'
33
+ needs: build
34
+ runs-on: ubuntu-latest
35
+ environment:
36
+ name: testpypi
37
+ url: https://test.pypi.org/p/the-machine
38
+ permissions:
39
+ id-token: write
40
+ steps:
41
+ - uses: actions/download-artifact@v4
42
+ with:
43
+ name: dist
44
+ path: dist/
45
+ - uses: pypa/gh-action-pypi-publish@release/v1
46
+ with:
47
+ repository-url: https://test.pypi.org/legacy/
48
+
49
+ pypi:
50
+ name: publish to PyPI
51
+ if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'pypi')
52
+ needs: build
53
+ runs-on: ubuntu-latest
54
+ environment:
55
+ name: pypi
56
+ url: https://pypi.org/p/the-machine
57
+ permissions:
58
+ id-token: write
59
+ steps:
60
+ - uses: actions/download-artifact@v4
61
+ with:
62
+ name: dist
63
+ path: dist/
64
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,25 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+
9
+ # uv
10
+ .venv/
11
+
12
+ # Tooling
13
+ .pytest_cache/
14
+ .mypy_cache/
15
+ .ruff_cache/
16
+ .coverage
17
+ htmlcov/
18
+
19
+ # TM local state
20
+ .aiagent/
21
+ *.local.toml
22
+ .env
23
+
24
+ # Reference project (local only)
25
+ pi/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 albert
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,371 @@
1
+ Metadata-Version: 2.5
2
+ Name: the-machine
3
+ Version: 0.1.0
4
+ Summary: The Machine (TM): a local AI agent that can control your machine
5
+ Project-URL: Homepage, https://github.com/beyondalbert/tm
6
+ Project-URL: Repository, https://github.com/beyondalbert/tm
7
+ Project-URL: Issues, https://github.com/beyondalbert/tm/issues
8
+ Author-email: albert <beyondalbert@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: agent,ai,automation,cli,coding-agent,llm
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: openai>=1.40
22
+ Requires-Dist: pydantic>=2.7
23
+ Requires-Dist: rich>=13.7
24
+ Requires-Dist: tomli-w>=1.0
25
+ Requires-Dist: typer>=0.12
26
+ Provides-Extra: all
27
+ Requires-Dist: anthropic>=0.39; extra == 'all'
28
+ Requires-Dist: google-genai>=0.3; extra == 'all'
29
+ Requires-Dist: textual>=0.80; extra == 'all'
30
+ Provides-Extra: anthropic
31
+ Requires-Dist: anthropic>=0.39; extra == 'anthropic'
32
+ Provides-Extra: google
33
+ Requires-Dist: google-genai>=0.3; extra == 'google'
34
+ Provides-Extra: tui
35
+ Requires-Dist: textual>=0.80; extra == 'tui'
36
+ Description-Content-Type: text/markdown
37
+
38
+ [English](README.md) | [简体中文](README.zh-CN.md)
39
+
40
+ # The Machine (TM)
41
+
42
+ A local AI agent that can control your machine, extensible and permission-gated.
43
+
44
+ TM is a Python agent harness inspired by the architecture of [pi](../pi), rebuilt
45
+ around explicit permission approval for filesystem, shell, and network access.
46
+
47
+ ## Status
48
+
49
+ Core agent, providers, built-in tools, permissions, and CLI are working. See
50
+ the phase table below.
51
+
52
+ ## Installation
53
+
54
+ TM is not on PyPI yet, so install it from the repository. [uv](https://docs.astral.sh/uv/)
55
+ is required (it can also install Python for you). Once released, the install
56
+ will simply be `uv tool install "the-machine[all]"`.
57
+
58
+ Install uv (once):
59
+
60
+ ```powershell
61
+ # Windows (PowerShell)
62
+ irm https://astral.sh/uv/install.ps1 | iex
63
+ ```
64
+
65
+ ```bash
66
+ # macOS / Linux
67
+ curl -LsSf https://astral.sh/uv/install.sh | sh
68
+ ```
69
+
70
+ ### Install the `tm` command (recommended)
71
+
72
+ ```bash
73
+ uv tool install "the-machine[all] @ git+https://github.com/beyondalbert/tm"
74
+ uv tool update-shell # add the tool bin dir to PATH; restart the terminal afterwards
75
+ tm --help
76
+ ```
77
+
78
+ `[all]` pulls in the Anthropic, Google, and TUI extras. Drop it for a minimal
79
+ install (OpenAI-compatible providers only; `--tui` then needs `textual`).
80
+ `git` must be installed for the `git+https` source. Without `git`, install from
81
+ the source archive instead:
82
+
83
+ ```bash
84
+ uv tool install "the-machine[all] @ https://github.com/beyondalbert/tm/archive/refs/heads/main.tar.gz"
85
+ ```
86
+
87
+ ### From a clone (development)
88
+
89
+ ```bash
90
+ git clone https://github.com/beyondalbert/tm
91
+ cd tm
92
+ uv sync --all-extras
93
+ uv run tm --help
94
+ ```
95
+
96
+ ### First run
97
+
98
+ ```bash
99
+ tm --login deepseek # store an API key (hidden input)
100
+ tm "list the files in this folder"
101
+ ```
102
+
103
+ Requires Python 3.11+. uv installs a suitable interpreter automatically.
104
+
105
+ ## Providers
106
+
107
+ Multi-provider via official SDK adapters. Domestic providers first (DeepSeek,
108
+ Qwen, Kimi, Zhipu GLM), then OpenAI and any OpenAI-compatible endpoint
109
+ (Ollama, vLLM, LM Studio).
110
+
111
+ Set the API key for the provider you want, then run:
112
+
113
+ ```powershell
114
+ $env:DEEPSEEK_API_KEY = "sk-..."
115
+ python -m uv run tm "list the files in this folder"
116
+ ```
117
+
118
+ ### Where to set the API key
119
+
120
+ Three options, in order of convenience:
121
+
122
+ 1. **Store it once (recommended)**. Prompts with hidden input and saves to the
123
+ credentials file:
124
+
125
+ ```powershell
126
+ python -m uv run tm --login deepseek
127
+ ```
128
+
129
+ Saved to `<config>/credentials.toml`:
130
+ - Windows: `%APPDATA%\the-machine\credentials.toml`
131
+ - Linux/macOS: `~/.config/the-machine/credentials.toml`
132
+
133
+ The file looks like:
134
+
135
+ ```toml
136
+ [providers]
137
+ deepseek = "sk-..."
138
+ qwen = "sk-..."
139
+ ```
140
+
141
+ 2. **Environment variable** (session or persistent):
142
+
143
+ ```powershell
144
+ # current shell only
145
+ $env:DEEPSEEK_API_KEY = "sk-..."
146
+ # persistent for your user (restart the terminal afterwards)
147
+ [Environment]::SetEnvironmentVariable("DEEPSEEK_API_KEY", "sk-...", "User")
148
+ ```
149
+
150
+ 3. **Override the config location** with `TM_CONFIG_DIR` if you do not want the
151
+ default directory.
152
+
153
+ Stored credentials take precedence over environment variables.
154
+
155
+ Providers and env vars:
156
+
157
+ | Provider | Id | Env var | Default model |
158
+ |---|---|---|---|
159
+ | DeepSeek | `deepseek` | `DEEPSEEK_API_KEY` | deepseek-chat |
160
+ | Qwen (DashScope) | `qwen` | `DASHSCOPE_API_KEY` | qwen-plus |
161
+ | Moonshot (Kimi) | `moonshot` | `MOONSHOT_API_KEY` | moonshot-v1-32k |
162
+ | Zhipu (GLM) | `zhipu` | `ZHIPUAI_API_KEY` | glm-4-plus |
163
+ | SiliconFlow | `siliconflow` | `SILICONFLOW_API_KEY` | DeepSeek-V3 |
164
+ | Anthropic | `anthropic` | `ANTHROPIC_API_KEY` | claude-3-5-sonnet-latest |
165
+ | Google Gemini | `google` | `GEMINI_API_KEY` | gemini-2.0-flash |
166
+ | OpenAI | `openai` | `OPENAI_API_KEY` | gpt-4o |
167
+ | Groq | `groq` | `GROQ_API_KEY` | llama-3.3-70b-versatile |
168
+ | OpenRouter | `openrouter` | `OPENROUTER_API_KEY` | claude-3.5-sonnet |
169
+ | Together AI | `together` | `TOGETHER_API_KEY` | Llama-3.3-70B |
170
+ | xAI | `xai` | `XAI_API_KEY` | grok-2-latest |
171
+ | Ollama (local) | `ollama` | – | qwen2.5:7b |
172
+
173
+ Anthropic and Google use their official SDKs; the rest use the OpenAI-compatible
174
+ adapter. `ollama` and other local servers need no key.
175
+
176
+ List models: `tm --list-models`.
177
+
178
+ ## Modes
179
+
180
+ | Command | Behavior |
181
+ |---|---|
182
+ | `tm` | Interactive agent REPL with tools and permissions |
183
+ | `tm "prompt"` | One-shot agent run |
184
+ | `tm --tui` | Textual TUI with approval modals |
185
+ | `tm --read-only` | Only read/grep/find/ls tools |
186
+ | `tm --no-tools` | Plain chat, no machine control |
187
+ | `tm --yolo` | Auto-approve every action |
188
+ | `tm -p` | One-shot, reads stdin when no prompt is given |
189
+ | `tm --json` | Emit agent events as JSON lines (for integration) |
190
+ | `tm -c` | Continue the most recent session in this directory |
191
+ | `tm -r` | Pick a saved session to resume (`--all-sessions` to include other dirs) |
192
+ | `tm --no-extensions` | Skip loading `.aiagent/extensions` |
193
+ | `tm --no-auto-compact` | Disable automatic context compaction |
194
+
195
+ Context files (`AGENTS.md` / `CLAUDE.md`, walking up from cwd, plus the global
196
+ config dir) are appended to the system prompt. Disable with `--no-context-files`.
197
+
198
+ ## Permissions
199
+
200
+ Every file, shell, and network action is evaluated against a policy. Deny rules
201
+ win, then allow rules, then the `default` decision (`ask` by default). Undecided
202
+ actions prompt the user; `a` remembers the decision for the session. All
203
+ decisions are appended to `<config>/audit.jsonl`.
204
+
205
+ Policy files are merged from `<config>/policy.toml` (global) and
206
+ `<cwd>/.aiagent/policy.toml` (project). Example:
207
+
208
+ ```toml
209
+ default = "ask"
210
+
211
+ [files.read]
212
+ allow = ["**"]
213
+ deny = ["**/.env", "**/id_rsa"]
214
+
215
+ [files.write]
216
+ allow = ["src/**", "tests/**"]
217
+ deny = [".git/**"]
218
+
219
+ [shell]
220
+ allow = ["git *", "ls *", "pytest *"]
221
+ deny = ["rm -rf *", "shutdown*", "format *"]
222
+
223
+ [network]
224
+ allow = ["api.deepseek.com"]
225
+ ```
226
+
227
+ Note: shell commands are matched textually. TM cannot reliably stop a shell
228
+ command from making network calls; use a sandbox/container when you need a hard
229
+ network boundary.
230
+
231
+ ## Interactive commands
232
+
233
+ Inside `tm` (agent REPL or TUI) type `/` for commands:
234
+
235
+ | Command | Description |
236
+ |---|---|
237
+ | `/help` | list commands |
238
+ | `/model [pattern]` | list providers or switch model |
239
+ | `/new` | start a new session |
240
+ | `/session` | show current session id/path |
241
+ | `/resume [n\|id]` | resume a saved session (`/resume` opens a picker) |
242
+ | `/tree [n]` | list conversation points, or branch from point n |
243
+ | `/fork [n]` | fork the session (at point n) into a new file |
244
+ | `/compact [note]` | summarize older context |
245
+ | `/skills` | list available skills |
246
+ | `/skill:<name>` | load a skill into the conversation |
247
+ | `/prompts` | list prompt templates |
248
+ | `/<template> [args]` | expand a prompt template |
249
+ | `/exit` | quit |
250
+
251
+ ## Sessions
252
+
253
+ Sessions are appended to JSONL files under `<config>/sessions/`, tagged with the
254
+ working directory. Resume them in several ways:
255
+
256
+ - `tm -c` — continue the most recent session for this directory.
257
+ - `tm -r` — list saved sessions (number, message count, updated time, preview)
258
+ and pick one; `--all-sessions` includes other directories.
259
+ - `tm --session <file>` — open a specific file.
260
+ - `tm --no-session` — run without persisting.
261
+
262
+ Inside a session, `/resume [n|id]` switches sessions (a picker modal in the TUI),
263
+ `/tree` navigates conversation points, and `/fork` copies a branch to a new file.
264
+
265
+ ## Settings
266
+
267
+ `<config>/settings.toml` (Windows: `%APPDATA%\the-machine\settings.toml`):
268
+
269
+ ```toml
270
+ provider = "deepseek"
271
+ model = "deepseek-chat"
272
+ temperature = 0.2
273
+ max_tokens = 8192
274
+ system_prompt = "Extra instructions appended to the system prompt."
275
+ auto_compact = true # summarize older context when nearing the limit
276
+ compact_threshold = 0.8 # fraction of the context window that triggers it
277
+ compact_keep_recent = 6 # recent messages kept verbatim
278
+ ```
279
+
280
+ Automatic compaction runs before a prompt when the estimated context exceeds
281
+ `context_window * compact_threshold`. Disable per run with `--no-auto-compact`.
282
+
283
+ ## Customization
284
+
285
+ **Context files.** `AGENTS.md` / `CLAUDE.md` (and `AGENTS.override.md`) are
286
+ loaded from the global config dir and walking up from the working directory, and
287
+ appended to the system prompt. Disable with `--no-context-files`.
288
+
289
+ **Skills.** A skill is a directory with a `SKILL.md` (optional frontmatter with
290
+ `name` and `description`). Load from `<config>/skills/`, `.agents/skills/`, or
291
+ `.aiagent/skills/`. Their names/descriptions are advertised in the system prompt;
292
+ load one with `/skill:<name>`.
293
+
294
+ ```markdown
295
+ ---
296
+ name: code-review
297
+ description: Review code for bugs and security issues
298
+ ---
299
+ Review the target for bugs, security issues, and missing tests.
300
+ ```
301
+
302
+ **Prompt templates.** Markdown files in `<config>/prompts/` or
303
+ `.aiagent/prompts/`, invoked as `/<filename>`; `{{input}}` is replaced by the
304
+ text after the command.
305
+
306
+ ```markdown
307
+ <!-- prompts/review.md -->
308
+ Review this for bugs, security, and performance: {{input}}
309
+ ```
310
+
311
+ **Extensions.** Python modules in `<config>/extensions/` or
312
+ `.aiagent/extensions/`. Each defines `setup(api)` and can register tools, event
313
+ listeners, and slash commands. Extensions run with full process permissions.
314
+
315
+ ```python
316
+ from tm.tools.base import Tool, text_result
317
+
318
+ def setup(api):
319
+ api.register_tool(MyTool())
320
+ api.on("tool_execution_end", lambda event: None)
321
+ api.register_command("greet", lambda arg: f"hello {arg}")
322
+ ```
323
+
324
+ ## Architecture
325
+
326
+ - `tm/ai` – unified multi-provider streaming LLM layer (types, event stream,
327
+ OpenAI-compatible + Anthropic + Google adapters, registry/catalog)
328
+ - `tm/core` – agent loop, high-level `Agent`, events, sessions, compaction
329
+ - `tm/tools` – built-in machine-control tools (read/write/edit/shell/grep/find/ls)
330
+ - `tm/permissions` – policy, allow/deny/ask checker, approval, audit log
331
+ - `tm/skills.py`, `tm/prompts.py`, `tm/extensions.py`, `tm/context` – resources
332
+ - `tm/tui` – terminal UI (Textual)
333
+ - `tm/cli` – Typer entry point and slash commands
334
+
335
+ ## Development
336
+
337
+ ```bash
338
+ python -m uv sync --all-extras # install dev + provider extras
339
+ python -m uv run pytest -q # tests
340
+ python -m uv run ruff check . # lint
341
+ python -m uv run mypy # types
342
+ python -m uv build # wheel + sdist
343
+ ```
344
+
345
+ `uv.lock` is committed. CI (`.github/workflows/ci.yml`) runs ruff and mypy on
346
+ Linux, and the test suite on Linux, Windows, and macOS for Python 3.11 and 3.12.
347
+ See [RELEASE.md](RELEASE.md) for publishing to PyPI.
348
+
349
+ ## Roadmap
350
+
351
+ | Phase | Scope | Status |
352
+ |---|---|---|
353
+ | P0 | scaffolding, config, CLI | done |
354
+ | P1 | ai layer + domestic providers | done |
355
+ | P2 | agent loop, tools, events | done |
356
+ | P3 | built-in tools | done |
357
+ | P4 | permission system + CLI wiring | done |
358
+ | P5 | Textual TUI | done |
359
+ | P6 | session persistence (JSONL tree) | done |
360
+ | P7 | Anthropic + Ollama providers | done |
361
+ | P8 | context files, skills, prompt templates, extensions | done |
362
+ | P9 | compaction, print/json mode, packaging | done |
363
+ | P10 | sessions: resume picker, fork, tree navigation | done |
364
+ | P11 | Google Gemini + extra providers, TUI streaming/status | done |
365
+
366
+ All planned phases are implemented. Possible future work: more providers,
367
+ provider-side prompt caching controls, a web UI, and multi-agent orchestration.
368
+
369
+ ## License
370
+
371
+ MIT, see [LICENSE](LICENSE).