makefile-agent 0.3.0__py3-none-any.whl
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.
- make_agent/__init__.py +0 -0
- make_agent/agent.py +190 -0
- make_agent/agent_shell.py +93 -0
- make_agent/app_dirs.py +51 -0
- make_agent/builtin_tools.py +380 -0
- make_agent/create_agent.py +228 -0
- make_agent/main.py +210 -0
- make_agent/memory.py +170 -0
- make_agent/parser.py +351 -0
- make_agent/settings.py +92 -0
- make_agent/templates/orchestra.mk +99 -0
- make_agent/tools.py +193 -0
- makefile_agent-0.3.0.dist-info/METADATA +265 -0
- makefile_agent-0.3.0.dist-info/RECORD +18 -0
- makefile_agent-0.3.0.dist-info/WHEEL +5 -0
- makefile_agent-0.3.0.dist-info/entry_points.txt +3 -0
- makefile_agent-0.3.0.dist-info/licenses/LICENSE +21 -0
- makefile_agent-0.3.0.dist-info/top_level.txt +1 -0
make_agent/tools.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Tool schema builder and executor for the make-agent.
|
|
2
|
+
|
|
3
|
+
Converts parsed Makefile rules into OpenAI-compatible tool definitions and
|
|
4
|
+
executes them by injecting parameters safely before invoking ``make``.
|
|
5
|
+
|
|
6
|
+
Parameter injection
|
|
7
|
+
-------------------
|
|
8
|
+
For each tool call, values are injected in two complementary ways:
|
|
9
|
+
|
|
10
|
+
1. **Temp file** (``PARAM_FILE``): Every parameter value is written to a
|
|
11
|
+
temporary file. Make receives ``PARAM_FILE=/tmp/...`` so recipes can
|
|
12
|
+
read arbitrary content::
|
|
13
|
+
|
|
14
|
+
write-file:
|
|
15
|
+
@cat "$(PARAM_FILE)" > "$(DEST)"
|
|
16
|
+
|
|
17
|
+
2. **params.mk** (``PARAM``): For single-line values, a temporary
|
|
18
|
+
``params.mk`` is also written with ``PARAM = <value>`` (``$`` signs
|
|
19
|
+
escaped as ``$$``) and loaded with ``-f params.mk``. Recipes that
|
|
20
|
+
reference ``$(PARAM)`` directly continue to work for simple values::
|
|
21
|
+
|
|
22
|
+
greet:
|
|
23
|
+
@echo "Hello, $(NAME)!"
|
|
24
|
+
|
|
25
|
+
The ``_FILE`` suffix form works for **all** values including multiline
|
|
26
|
+
text; the bare form is a convenience for the common single-line case.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import json
|
|
32
|
+
import logging
|
|
33
|
+
import re
|
|
34
|
+
import subprocess
|
|
35
|
+
import tempfile
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
from typing import Any
|
|
38
|
+
|
|
39
|
+
from make_agent.parser import Makefile, Param
|
|
40
|
+
|
|
41
|
+
logger = logging.getLogger(__name__)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
_VALID_MAKE_VAR_NAME_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _is_valid_make_var_name(name: str) -> bool:
|
|
48
|
+
return bool(_VALID_MAKE_VAR_NAME_RE.fullmatch(name))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _escape_make_value(value: str) -> str:
|
|
52
|
+
"""Escape *value* for use on the right-hand side of a Make ``=`` assignment.
|
|
53
|
+
|
|
54
|
+
Only ``$`` needs escaping: ``$`` → ``$$``. Everything else (quotes,
|
|
55
|
+
backslashes, spaces) is safe in a Make variable value.
|
|
56
|
+
"""
|
|
57
|
+
return value.replace("$", "$$")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def format_tool_result(stdout: str, stderr: str, exit_code: int | None, max_output: int = 0) -> str:
|
|
61
|
+
"""Serialise tool output as a JSON string for the LLM.
|
|
62
|
+
|
|
63
|
+
*max_output* limits how many characters of *stdout* and *stderr* are kept
|
|
64
|
+
(each stream capped independently). When a stream is longer, the excess is
|
|
65
|
+
dropped and an ``omitted_chars`` key is added so the LLM knows it received
|
|
66
|
+
a partial result. ``0`` means no limit.
|
|
67
|
+
"""
|
|
68
|
+
omitted = 0
|
|
69
|
+
if max_output > 0 and len(stdout) > max_output:
|
|
70
|
+
omitted += len(stdout) - max_output
|
|
71
|
+
stdout = stdout[:max_output]
|
|
72
|
+
if max_output > 0 and len(stderr) > max_output:
|
|
73
|
+
omitted += len(stderr) - max_output
|
|
74
|
+
stderr = stderr[:max_output]
|
|
75
|
+
result: dict[str, Any] = {"stdout": stdout, "stderr": stderr, "exit_code": exit_code}
|
|
76
|
+
if omitted:
|
|
77
|
+
result["omitted_chars"] = omitted
|
|
78
|
+
return json.dumps(result)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _param_schema(p: Param) -> dict[str, str]:
|
|
82
|
+
"""Return the JSON Schema fragment for a single tool parameter."""
|
|
83
|
+
json_type = p.type if p.type in ("string", "number", "integer", "boolean") else "string"
|
|
84
|
+
return {"type": json_type, "description": p.description}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def build_tools(makefile: Makefile) -> list[dict[str, Any]]:
|
|
88
|
+
"""Return a list of OpenAI function-tool dicts for every rule that has a
|
|
89
|
+
``# <tool>`` description block."""
|
|
90
|
+
tools = []
|
|
91
|
+
for rule in makefile.rules:
|
|
92
|
+
if rule.description is None:
|
|
93
|
+
continue
|
|
94
|
+
properties = {p.name: _param_schema(p) for p in rule.params}
|
|
95
|
+
tools.append(
|
|
96
|
+
{
|
|
97
|
+
"type": "function",
|
|
98
|
+
"function": {
|
|
99
|
+
"name": rule.target,
|
|
100
|
+
"description": rule.description,
|
|
101
|
+
"parameters": {
|
|
102
|
+
"type": "object",
|
|
103
|
+
"properties": properties,
|
|
104
|
+
"required": [p.name for p in rule.params],
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
return tools
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def run_tool(
|
|
113
|
+
target: str,
|
|
114
|
+
arguments: dict[str, Any],
|
|
115
|
+
makefile_path: Path,
|
|
116
|
+
timeout: int = 600,
|
|
117
|
+
max_output: int = 0,
|
|
118
|
+
) -> str:
|
|
119
|
+
"""Invoke ``make`` with safely injected parameters and return output as JSON.
|
|
120
|
+
|
|
121
|
+
Returns a JSON string with keys ``stdout``, ``stderr``, and ``exit_code``
|
|
122
|
+
(``null`` for framework-level errors such as timeout or OS failure). When
|
|
123
|
+
*max_output* is non-zero and the stdout exceeds that limit, the output is
|
|
124
|
+
truncated and an ``omitted_chars`` key is added.
|
|
125
|
+
|
|
126
|
+
Every argument value is written to a temporary file; Make receives
|
|
127
|
+
``PARAM_FILE=/tmp/...`` for each parameter. For single-line values
|
|
128
|
+
that contain no newlines, a temporary ``params.mk`` is also written
|
|
129
|
+
with ``PARAM = <escaped-value>`` so recipes can use ``$(PARAM)``
|
|
130
|
+
directly without any quoting ceremony.
|
|
131
|
+
|
|
132
|
+
All temporary files are always removed after the subprocess exits.
|
|
133
|
+
"""
|
|
134
|
+
tmp_files: list[Path] = []
|
|
135
|
+
try:
|
|
136
|
+
for k in arguments:
|
|
137
|
+
if not _is_valid_make_var_name(k):
|
|
138
|
+
return format_tool_result("", f"{k!r} is not a valid make variable name", None)
|
|
139
|
+
|
|
140
|
+
params_mk_lines: list[str] = []
|
|
141
|
+
make_vars: list[str] = []
|
|
142
|
+
|
|
143
|
+
for k, v in arguments.items():
|
|
144
|
+
# Normalise JSON primitives (int, float, bool) to str.
|
|
145
|
+
v_str = str(v)
|
|
146
|
+
# Always write a temp file for $(PARAM_FILE) access.
|
|
147
|
+
with tempfile.NamedTemporaryFile(
|
|
148
|
+
mode="w",
|
|
149
|
+
prefix=f"make-agent-{k}-",
|
|
150
|
+
suffix=".tmp",
|
|
151
|
+
delete=False,
|
|
152
|
+
) as tf:
|
|
153
|
+
tf.write(v_str)
|
|
154
|
+
file_path = Path(tf.name)
|
|
155
|
+
tmp_files.append(file_path)
|
|
156
|
+
make_vars.append(f"{k}_FILE={file_path}")
|
|
157
|
+
|
|
158
|
+
# Also provide $(PARAM) for single-line values via params.mk.
|
|
159
|
+
if "\n" not in v_str:
|
|
160
|
+
params_mk_lines.append(f"{k} = {_escape_make_value(v_str)}")
|
|
161
|
+
|
|
162
|
+
cmd: list[str] = ["make", "--no-print-directory"]
|
|
163
|
+
|
|
164
|
+
if params_mk_lines:
|
|
165
|
+
params_content = "\n".join(params_mk_lines) + "\n"
|
|
166
|
+
with tempfile.NamedTemporaryFile(
|
|
167
|
+
mode="w",
|
|
168
|
+
prefix="make-agent-params-",
|
|
169
|
+
suffix=".mk",
|
|
170
|
+
delete=False,
|
|
171
|
+
) as tf:
|
|
172
|
+
tf.write(params_content)
|
|
173
|
+
params_mk = Path(tf.name)
|
|
174
|
+
tmp_files.append(params_mk)
|
|
175
|
+
cmd += ["-f", str(params_mk)]
|
|
176
|
+
|
|
177
|
+
cmd += ["-f", str(makefile_path), target] + make_vars
|
|
178
|
+
|
|
179
|
+
logger.debug(f"running tool with command: {' '.join(cmd)}")
|
|
180
|
+
try:
|
|
181
|
+
result = subprocess.run(cmd, capture_output=True, text=True, stdin=subprocess.DEVNULL, timeout=timeout)
|
|
182
|
+
logger.debug(f"result of '{' '.join(cmd)}': exit {result.returncode}, stdout: {result.stdout!r}, stderr: {result.stderr!r}")
|
|
183
|
+
except subprocess.TimeoutExpired:
|
|
184
|
+
return format_tool_result("", f"tool '{target}' exceeded {timeout}s limit", None)
|
|
185
|
+
except OSError as e:
|
|
186
|
+
return format_tool_result("", f"failed to run make: {e}", None)
|
|
187
|
+
return format_tool_result(result.stdout, result.stderr, result.returncode, max_output)
|
|
188
|
+
finally:
|
|
189
|
+
for tmp in tmp_files:
|
|
190
|
+
try:
|
|
191
|
+
tmp.unlink()
|
|
192
|
+
except OSError:
|
|
193
|
+
pass
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: makefile-agent
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: AI‑assistant‑as‑Makefile: a tool to create and manage AI agents using a simple YAML configuration.
|
|
5
|
+
Author: Dmitriy Sorochenkov
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dmtr/make-agent
|
|
8
|
+
Project-URL: Repository, https://github.com/dmtr/make-agent
|
|
9
|
+
Project-URL: Issues, https://github.com/dmtr/make-agent/issues
|
|
10
|
+
Keywords: ai,agent,makefile,llm,automation
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: any-llm-sdk[anthropic,ollama,openai,openrouter]==1.13.0
|
|
23
|
+
Requires-Dist: pyyaml>=6
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# make-agent
|
|
27
|
+
|
|
28
|
+
An AI agent whose system prompt and tools are defined in a Makefile.
|
|
29
|
+
|
|
30
|
+
Each Makefile target annotated with a `# <tool>` comment block becomes a callable tool. The agent invokes targets via `make`, passing parameters as `KEY=value` arguments. A `define SYSTEM_PROMPT` block sets the agent's system prompt.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
uv pip install .
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Requires Python 3.11+ and a working `make` binary. Uses [any-llm-sdk](https://pypi.org/project/any-llm-sdk/) for model access, so any API keys (e.g. `ANTHROPIC_API_KEY`) must be set in the environment.
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
ANTHROPIC_API_KEY=<key> uv run make_agent [run] [-f FILE] [--model MODEL] [--prompt PROMPT | --prompt-file FILE] [--with-memory]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- `-f FILE` — Makefile to load. Searched in the current directory first, then `~/.make-agent/<project>/agents/`. Defaults to the value in `settings.yaml`, or `./Makefile` if not set.
|
|
47
|
+
- `--model MODEL` — any-llm model string. Defaults to the value in `settings.yaml`.
|
|
48
|
+
- `--prompt PROMPT` — send a single prompt and exit instead of entering the interactive shell
|
|
49
|
+
- `--prompt-file FILE` — send a single prompt read from `FILE` and exit
|
|
50
|
+
- `--agents-dir DIR` — directory for specialist `.mk` files (default: `~/.make-agent/<project>/agents/`)
|
|
51
|
+
- `--agent-model MODEL` — model used when running specialist agents via `run_agent` (default: same as `--model`)
|
|
52
|
+
- `--with-memory` — enable persistent conversation memory (see [Memory](#memory))
|
|
53
|
+
- `--disable-builtin-tools TOOLS` — comma-separated built-in tool names to disable, or `all`
|
|
54
|
+
- `--max-tool-output CHARS` — truncate tool stdout to this many characters; `0` = unlimited (default: 20000)
|
|
55
|
+
- `--max-tokens N` — max tokens in the model response (default: 4096)
|
|
56
|
+
- `--max-retries N` — max retry attempts on rate limit errors (default: 5)
|
|
57
|
+
- `--tool-timeout SECONDS` — timeout for each tool subprocess (default: 600)
|
|
58
|
+
- `--debug` — write all messages to `~/.make-agent/<project>/logs/make-agent.log`
|
|
59
|
+
|
|
60
|
+
Without `--prompt`, the agent starts an interactive REPL. Type `exit`, `quit`, or press Ctrl-D to leave.
|
|
61
|
+
|
|
62
|
+
### First run — setup wizard
|
|
63
|
+
|
|
64
|
+
If no `settings.yaml` exists for the project and no Makefile is found automatically, the agent prompts you to create one:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
No settings.yaml found for this project.
|
|
68
|
+
Let's create one. Press Enter to accept the default shown in brackets.
|
|
69
|
+
|
|
70
|
+
Makefile path [Makefile]: ./my-agent.mk
|
|
71
|
+
Model [anthropic/claude-haiku-4-5-20251001]:
|
|
72
|
+
|
|
73
|
+
Saved settings to ~/.make-agent/Users_alice_proj_myapp/settings.yaml
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Project settings
|
|
77
|
+
|
|
78
|
+
All per-project data is stored under `~/.make-agent/`:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
~/.make-agent/
|
|
82
|
+
└── <project-slug>/ # e.g. Users_alice_proj_myapp
|
|
83
|
+
├── settings.yaml # default model and Makefile
|
|
84
|
+
├── memory.db # conversation history (when memory is enabled)
|
|
85
|
+
├── agents/ # specialist agent .mk files
|
|
86
|
+
└── logs/
|
|
87
|
+
└── make-agent.log # debug log (written when --debug is set)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The **project slug** is the absolute path of the working directory with the leading `/` stripped and remaining `/` replaced by `_`.
|
|
91
|
+
|
|
92
|
+
### settings.yaml
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
model: anthropic/claude-haiku-4-5-20251001
|
|
96
|
+
makefile: ./my-agent.mk
|
|
97
|
+
memory: true # optional — enable persistent memory
|
|
98
|
+
agent_model: anthropic/claude-opus-4-5 # optional — model for run_agent calls
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
All fields are optional. CLI flags always take precedence over `settings.yaml` values.
|
|
102
|
+
|
|
103
|
+
## Makefile format
|
|
104
|
+
|
|
105
|
+
```makefile
|
|
106
|
+
define SYSTEM_PROMPT
|
|
107
|
+
You are a filesystem assistant.
|
|
108
|
+
endef
|
|
109
|
+
|
|
110
|
+
.PHONY: list-files greet
|
|
111
|
+
|
|
112
|
+
# <tool>
|
|
113
|
+
# List files in a directory.
|
|
114
|
+
# @param DIR string The directory path to list
|
|
115
|
+
# </tool>
|
|
116
|
+
list-files:
|
|
117
|
+
@ls -la $(DIR)
|
|
118
|
+
|
|
119
|
+
# <tool>
|
|
120
|
+
# Greet someone.
|
|
121
|
+
# @param NAME string The name to greet
|
|
122
|
+
# </tool>
|
|
123
|
+
greet:
|
|
124
|
+
@echo "Hello, $(NAME)!"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Special comment blocks
|
|
128
|
+
|
|
129
|
+
- `define SYSTEM_PROMPT ... endef` sets the system prompt passed to the model. The content is raw text — no `#` prefix needed. `endef` must be on its own line with no indentation.
|
|
130
|
+
- `# <tool> ... # </tool>` marks the following target as an LLM-callable tool. Lines starting with `# @param NAME type description` declare parameters (JSON Schema primitives: `string`, `number`, `integer`, `boolean`). All other lines form the tool description.
|
|
131
|
+
|
|
132
|
+
### Parameters and `$(PARAM_FILE)`
|
|
133
|
+
|
|
134
|
+
Every declared parameter automatically gets two Make variables injected at call time:
|
|
135
|
+
|
|
136
|
+
- `$(PARAM)` — the value as a Make variable with shell-escaped value. Convenient for single-line values.
|
|
137
|
+
- `$(PARAM_FILE)` — path to a temp file containing the full, unescaped value. Use this for multiline content or when the value might contain shell metacharacters.
|
|
138
|
+
|
|
139
|
+
```makefile
|
|
140
|
+
# <tool>
|
|
141
|
+
# Write content to a file.
|
|
142
|
+
# @param FILE_PATH string Destination file path
|
|
143
|
+
# @param CONTENT string Content to write (may be multiline)
|
|
144
|
+
# </tool>
|
|
145
|
+
write-file:
|
|
146
|
+
@cat "$(CONTENT_FILE)" > "$(FILE_PATH)"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Targets without a `# <tool>` block are invisible to the model.
|
|
150
|
+
|
|
151
|
+
## Built-in tools
|
|
152
|
+
|
|
153
|
+
Every agent automatically receives four built-in tools alongside its Makefile-defined tools — no Makefile declaration needed:
|
|
154
|
+
|
|
155
|
+
| Tool | What it does |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `list_agent` | Scan the agents directory and return each specialist's name and description |
|
|
158
|
+
| `validate_agent` | Parse and validate a named specialist's Makefile, reporting any errors |
|
|
159
|
+
| `create_agent` | Generate a new `.mk` file from a YAML spec and save it to the agents directory |
|
|
160
|
+
| `run_agent` | Delegate a task to a specialist agent and return its output |
|
|
161
|
+
|
|
162
|
+
The agents directory defaults to `~/.make-agent/<project>/agents/` and can be changed with `--agents-dir`.
|
|
163
|
+
|
|
164
|
+
## Memory
|
|
165
|
+
|
|
166
|
+
Agents can persist every conversation turn to a local SQLite database and search it in future sessions.
|
|
167
|
+
|
|
168
|
+
### Enabling memory
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# One-time flag
|
|
172
|
+
make_agent --with-memory -f my-agent.mk
|
|
173
|
+
|
|
174
|
+
# Always on for this project (settings.yaml)
|
|
175
|
+
memory: true
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The database is stored at `~/.make-agent/<project-slug>/memory.db`. Every user message and final agent reply is written automatically.
|
|
179
|
+
|
|
180
|
+
The project directory layout becomes:
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
~/.make-agent/
|
|
184
|
+
└── <project-slug>/
|
|
185
|
+
├── settings.yaml
|
|
186
|
+
├── memory.db # conversation history (created on first use)
|
|
187
|
+
├── agents/
|
|
188
|
+
└── logs/
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Memory tools
|
|
192
|
+
|
|
193
|
+
When memory is enabled, three additional built-in tools are injected:
|
|
194
|
+
|
|
195
|
+
| Tool | What it does |
|
|
196
|
+
|---|---|
|
|
197
|
+
| `get_recent_messages(limit)` | Return the N most recent messages in chronological order |
|
|
198
|
+
| `search_user_memory(query, limit, from_date, to_date)` | FTS5 keyword search over past user messages |
|
|
199
|
+
| `search_agent_memory(query, limit, from_date, to_date)` | FTS5 keyword search over past agent replies |
|
|
200
|
+
|
|
201
|
+
**FTS5 search tips** — the search is keyword-based, not semantic:
|
|
202
|
+
|
|
203
|
+
- Use short keywords, not sentences: `"goal project"` not `"what is the goal of this project"`
|
|
204
|
+
- Use `OR` for broader recall: `"goal OR objective OR purpose"`
|
|
205
|
+
- Stop words (`the`, `of`, `is`, `a`) are not indexed — omit them
|
|
206
|
+
- If a search returns nothing, retry with broader or alternative keywords
|
|
207
|
+
- Use `get_recent_messages` when you need recent context and don't know which keywords to search for
|
|
208
|
+
|
|
209
|
+
### Orchestrator pattern
|
|
210
|
+
|
|
211
|
+
`examples/orchestra.mk` shows how to use the built-in tools to build a self-managing agent that creates and improves specialist agents at runtime:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Interactive session
|
|
215
|
+
make_agent -f examples/orchestra.mk
|
|
216
|
+
|
|
217
|
+
# Single prompt
|
|
218
|
+
make_agent -f examples/orchestra.mk --prompt "Summarise the git log for the last week"
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
For every task the orchestrator:
|
|
222
|
+
|
|
223
|
+
1. Calls `list_agent` to discover available specialists.
|
|
224
|
+
2. Delegates to an existing specialist with `run_agent`, or designs and saves a new one with `create_agent` first.
|
|
225
|
+
3. Improves any specialist by calling `create_agent` with the same name — it overwrites the previous version.
|
|
226
|
+
|
|
227
|
+
### YAML spec for `create_agent`
|
|
228
|
+
|
|
229
|
+
```yaml
|
|
230
|
+
system_prompt: "You are a specialist that searches source code for patterns."
|
|
231
|
+
tools:
|
|
232
|
+
- name: search-files
|
|
233
|
+
description: Search files for a text pattern and return matching lines.
|
|
234
|
+
params:
|
|
235
|
+
- name: PATTERN
|
|
236
|
+
type: string
|
|
237
|
+
description: The text pattern to search for
|
|
238
|
+
- name: DIR
|
|
239
|
+
type: string
|
|
240
|
+
description: The directory to search in
|
|
241
|
+
recipe:
|
|
242
|
+
- '@grep -rn "$(PATTERN)" "$(DIR)" || echo "No matches found"'
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
`make-agent-create` converts this spec into a standard `make-agent` Makefile.
|
|
246
|
+
|
|
247
|
+
## Example
|
|
248
|
+
|
|
249
|
+
`examples/orchestra.mk` is the orchestrator agent — it manages specialist agents using the built-in tools and also exposes three simple no-parameter tools for system information:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
make_agent -f examples/orchestra.mk
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
| Tool | Recipe |
|
|
256
|
+
|---|---|
|
|
257
|
+
| `current-dir` | `pwd` |
|
|
258
|
+
| `os-info` | `uname -a` |
|
|
259
|
+
| `current-date` | `date` |
|
|
260
|
+
|
|
261
|
+
## Running tests
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
uv run pytest
|
|
265
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
make_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
make_agent/agent.py,sha256=lPPvCzWa_LrOGT38W-P2qjq7KmNyewtnUPfj18J4suA,7937
|
|
3
|
+
make_agent/agent_shell.py,sha256=O29TibjzwN5vUhoVAnS2ZVv5V0hp6uoeRSFJGMWnXc4,2765
|
|
4
|
+
make_agent/app_dirs.py,sha256=JCk0Q9nwgQ5hYpRYCvkcE0xkSq6ljMzsP7Z4fGtTJh4,1803
|
|
5
|
+
make_agent/builtin_tools.py,sha256=jxJerZ1OIa-PjnkqcGG2GNjjQ-AVOymU5LmRKvoy1Ss,14978
|
|
6
|
+
make_agent/create_agent.py,sha256=I0L2o1y-DoEo9F8Rk_4iTsuWrEZYVZJNHCvgqKeS4KQ,7343
|
|
7
|
+
make_agent/main.py,sha256=7Nt1MhosTD-SQ8tSddEHIrPZFg6SVp2l4PnUCczn2WY,9175
|
|
8
|
+
make_agent/memory.py,sha256=hiQJpt-O5SXETC-iKgTIWgsymT_2faGFTvoVHgtQoJA,5529
|
|
9
|
+
make_agent/parser.py,sha256=YcbLacZfa6O3f88vYVpE9pOldPHVNiQ1KGC564YJxQs,13620
|
|
10
|
+
make_agent/settings.py,sha256=r31KZDJLOB3U2Vpj0x3ZmbsZVOFrT58tiG9Bmw2LDVM,3264
|
|
11
|
+
make_agent/tools.py,sha256=rUBpJ-3m1xRZtlzU9IGmUShBaDfCf7AJnNeXmQ_CTQM,7110
|
|
12
|
+
make_agent/templates/orchestra.mk,sha256=6s-1TrQ8mbvXbJuQhg8grkGO0mCqqetvDoXHLXE8OwI,4232
|
|
13
|
+
makefile_agent-0.3.0.dist-info/licenses/LICENSE,sha256=AMd6ov3p3D8YgzLdrgxkuVeYD96ngOr9QrUcR58gTJY,1076
|
|
14
|
+
makefile_agent-0.3.0.dist-info/METADATA,sha256=OZAWDiqoVgxmK_vR9ImIQF42otC6qPodVMuiRNymp_U,9895
|
|
15
|
+
makefile_agent-0.3.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
16
|
+
makefile_agent-0.3.0.dist-info/entry_points.txt,sha256=YTKP5Yp1oBx_GBi4jyr0B1BqxLPjL3e2jXmMezs2rtQ,101
|
|
17
|
+
makefile_agent-0.3.0.dist-info/top_level.txt,sha256=TKwBMo6fYjF9KTNhHojXhp7jd0_w3pHxw34XVi7U0_g,11
|
|
18
|
+
makefile_agent-0.3.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dmitriy Sorochenkov
|
|
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 @@
|
|
|
1
|
+
make_agent
|