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.
- the_machine-0.1.0/.github/workflows/ci.yml +63 -0
- the_machine-0.1.0/.github/workflows/publish.yml +64 -0
- the_machine-0.1.0/.gitignore +25 -0
- the_machine-0.1.0/LICENSE +21 -0
- the_machine-0.1.0/PKG-INFO +371 -0
- the_machine-0.1.0/README.md +334 -0
- the_machine-0.1.0/README.zh-CN.md +322 -0
- the_machine-0.1.0/RELEASE.md +93 -0
- the_machine-0.1.0/pyproject.toml +85 -0
- the_machine-0.1.0/scripts/smoke_deepseek.py +106 -0
- the_machine-0.1.0/src/tm/__init__.py +3 -0
- the_machine-0.1.0/src/tm/ai/__init__.py +29 -0
- the_machine-0.1.0/src/tm/ai/catalog.py +193 -0
- the_machine-0.1.0/src/tm/ai/event_stream.py +67 -0
- the_machine-0.1.0/src/tm/ai/providers/__init__.py +13 -0
- the_machine-0.1.0/src/tm/ai/providers/anthropic.py +289 -0
- the_machine-0.1.0/src/tm/ai/providers/base.py +51 -0
- the_machine-0.1.0/src/tm/ai/providers/google.py +253 -0
- the_machine-0.1.0/src/tm/ai/providers/openai_compat.py +303 -0
- the_machine-0.1.0/src/tm/ai/registry.py +161 -0
- the_machine-0.1.0/src/tm/ai/types.py +200 -0
- the_machine-0.1.0/src/tm/cli/__init__.py +0 -0
- the_machine-0.1.0/src/tm/cli/commands.py +326 -0
- the_machine-0.1.0/src/tm/cli/console_ui.py +84 -0
- the_machine-0.1.0/src/tm/cli/main.py +557 -0
- the_machine-0.1.0/src/tm/config.py +106 -0
- the_machine-0.1.0/src/tm/context/__init__.py +3 -0
- the_machine-0.1.0/src/tm/context/agents_md.py +58 -0
- the_machine-0.1.0/src/tm/core/__init__.py +20 -0
- the_machine-0.1.0/src/tm/core/agent.py +330 -0
- the_machine-0.1.0/src/tm/core/compaction.py +80 -0
- the_machine-0.1.0/src/tm/core/events.py +84 -0
- the_machine-0.1.0/src/tm/core/loop.py +117 -0
- the_machine-0.1.0/src/tm/core/session.py +244 -0
- the_machine-0.1.0/src/tm/core/system_prompt.py +22 -0
- the_machine-0.1.0/src/tm/extensions.py +86 -0
- the_machine-0.1.0/src/tm/permissions/__init__.py +36 -0
- the_machine-0.1.0/src/tm/permissions/actions.py +36 -0
- the_machine-0.1.0/src/tm/permissions/approval.py +60 -0
- the_machine-0.1.0/src/tm/permissions/audit.py +43 -0
- the_machine-0.1.0/src/tm/permissions/gate.py +82 -0
- the_machine-0.1.0/src/tm/permissions/policy.py +134 -0
- the_machine-0.1.0/src/tm/prompts.py +58 -0
- the_machine-0.1.0/src/tm/py.typed +0 -0
- the_machine-0.1.0/src/tm/skills.py +96 -0
- the_machine-0.1.0/src/tm/tools/__init__.py +34 -0
- the_machine-0.1.0/src/tm/tools/base.py +78 -0
- the_machine-0.1.0/src/tm/tools/edit.py +59 -0
- the_machine-0.1.0/src/tm/tools/find.py +84 -0
- the_machine-0.1.0/src/tm/tools/grep.py +106 -0
- the_machine-0.1.0/src/tm/tools/ls.py +41 -0
- the_machine-0.1.0/src/tm/tools/path_utils.py +13 -0
- the_machine-0.1.0/src/tm/tools/read.py +50 -0
- the_machine-0.1.0/src/tm/tools/registry.py +31 -0
- the_machine-0.1.0/src/tm/tools/shell.py +136 -0
- the_machine-0.1.0/src/tm/tools/truncate.py +25 -0
- the_machine-0.1.0/src/tm/tools/write.py +30 -0
- the_machine-0.1.0/src/tm/tui/__init__.py +15 -0
- the_machine-0.1.0/src/tm/tui/app.py +241 -0
- the_machine-0.1.0/src/tm/utils/__init__.py +5 -0
- the_machine-0.1.0/src/tm/utils/abort.py +28 -0
- the_machine-0.1.0/tests/test_agent.py +174 -0
- the_machine-0.1.0/tests/test_anthropic.py +191 -0
- the_machine-0.1.0/tests/test_commands.py +262 -0
- the_machine-0.1.0/tests/test_compaction.py +122 -0
- the_machine-0.1.0/tests/test_config.py +42 -0
- the_machine-0.1.0/tests/test_context.py +42 -0
- the_machine-0.1.0/tests/test_event_stream.py +39 -0
- the_machine-0.1.0/tests/test_extensions.py +93 -0
- the_machine-0.1.0/tests/test_google.py +169 -0
- the_machine-0.1.0/tests/test_openai_compat.py +224 -0
- the_machine-0.1.0/tests/test_permissions.py +159 -0
- the_machine-0.1.0/tests/test_prompts.py +41 -0
- the_machine-0.1.0/tests/test_registry.py +85 -0
- the_machine-0.1.0/tests/test_session.py +186 -0
- the_machine-0.1.0/tests/test_skills.py +62 -0
- the_machine-0.1.0/tests/test_smoke_deepseek.py +32 -0
- the_machine-0.1.0/tests/test_tools.py +132 -0
- the_machine-0.1.0/tests/test_tui.py +165 -0
- 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).
|