monkeybot-cli 0.10.0__tar.gz → 0.11.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.
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/.gitignore +5 -1
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/PKG-INFO +2 -2
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/pyproject.toml +2 -2
- monkeybot_cli-0.11.0/src/monkeybot_cli/commands/mcp_login.py +81 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/commands/new.py +44 -4
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/main.py +13 -1
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold.py +86 -16
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/command_allowlist.yaml +2 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/monkeybot.example.yaml +15 -0
- monkeybot_cli-0.11.0/src/monkeybot_cli/scaffold_defaults/templates/coding/AGENT.md +35 -0
- monkeybot_cli-0.11.0/src/monkeybot_cli/scaffold_defaults/templates/coding/command_allowlist.yaml +92 -0
- monkeybot_cli-0.11.0/src/monkeybot_cli/scaffold_defaults/templates/coding/monkeybot.overrides.yaml +31 -0
- monkeybot_cli-0.11.0/src/monkeybot_cli/scaffold_defaults/templates/coding/permissions.yaml +42 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_cli.py +44 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_scaffold.py +193 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/uv.lock +2 -1
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/skills/monkeybot/SKILL.md +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/skills/monkeybot/references/config-sections.md +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/__init__.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/chat_file_index.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/chat_local_shell.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/chat_renderer.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/chat_session.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/chat_status_bar.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/chat_theme.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/chat_tool_display.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/chat_tui.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/chat_tui_widgets.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/commands/__init__.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/commands/chat.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/commands/doctor.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/commands/loop.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/commands/refresh.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/commands/run_cmd.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/commands/talk.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/commands/trace.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/commands/validate.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/compat.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/config_resolve.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/exit_commands.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/extras_catalog.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/gateway_health.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/opensandbox_lifecycle.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/output.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/process_tree.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/providers.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/realtime/__init__.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/realtime/audio_io.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/realtime/client.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/realtime/gateway_manager.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/realtime/push_to_talk.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/realtime/session.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/realtime/session_controller.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/realtime/talk_ui.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/realtime/wire_encode.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/runtime_python.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/AGENT.md +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/Dockerfile +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/__init__.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/browser/SKILL.md +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/dockerignore +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/env.example +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/goal/SKILL.md +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/image-generator/SKILL.md +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/image-generator/generate_image.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/loop/SKILL.md +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/mcp.json +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/opensandbox.docker.toml +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/otel-collector.example.yaml +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/scaffold_defaults/permissions.yaml +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/session_controller.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/src/monkeybot_cli/terminal_markdown.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_chat_continue.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_chat_e2e.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_chat_errors.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_chat_file_index.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_chat_grounding.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_chat_local_shell.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_chat_renderer_parity.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_chat_session.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_chat_status_bar.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_chat_theme.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_chat_tool_display.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_chat_tui.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_config_resolve.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_doctor.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_extras_catalog.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_gateway_health.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_opensandbox_lifecycle.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_realtime_session_controller.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_run_cmd.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_runtime_python.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_terminal_markdown.py +0 -0
- {monkeybot_cli-0.10.0 → monkeybot_cli-0.11.0}/tests/test_trace_cmd.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: monkeybot-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: CLI to create, configure, validate, and chat with monkeybot agents.
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: httpx>=0.27.0
|
|
7
7
|
Requires-Dist: monkeybot-browser-mcp<1,>=0.5.0
|
|
8
|
-
Requires-Dist: monkeybot[cli]<4,>=3.
|
|
8
|
+
Requires-Dist: monkeybot[cli]<4,>=3.10.0
|
|
9
9
|
Requires-Dist: python-dotenv>=1.0.0
|
|
10
10
|
Requires-Dist: pyyaml>=6.0.2
|
|
11
11
|
Requires-Dist: textual>=8.2.8
|
|
@@ -4,13 +4,13 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "monkeybot-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.11.0"
|
|
8
8
|
description = "CLI to create, configure, validate, and chat with monkeybot agents."
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
dependencies = [
|
|
11
11
|
# `cli` pulls realtime + typer so `monkeybot talk` works out of the box (text mode).
|
|
12
12
|
# Audio mic/PTT needs the agent (or this env) to also install monkeybot[cli-realtime].
|
|
13
|
-
"monkeybot[cli]>=3.
|
|
13
|
+
"monkeybot[cli]>=3.10.0,<4",
|
|
14
14
|
"monkeybot-browser-mcp>=0.5.0,<1",
|
|
15
15
|
"httpx>=0.27.0",
|
|
16
16
|
"pyyaml>=6.0.2",
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""monkeybot mcp-login — browser sign-in for one remote MCP server."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import asyncio
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from mcp.client.auth.exceptions import OAuthFlowError
|
|
14
|
+
from monkeybot.core.mcp.mcp_client import MCPAuthError
|
|
15
|
+
from monkeybot.core.mcp.oauth_flow import login_mcp_server
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
CLIENT_SECRET_ENV = "MONKEYBOT_MCP_CLIENT_SECRET"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def run_mcp_login(args: argparse.Namespace) -> int:
|
|
23
|
+
"""Sign in, print JSON progress on stdout, and return 0 when the attempt finishes."""
|
|
24
|
+
client_secret = args.client_secret or os.environ.get(CLIENT_SECRET_ENV, "")
|
|
25
|
+
try:
|
|
26
|
+
result = asyncio.run(
|
|
27
|
+
login_mcp_server(
|
|
28
|
+
args.url,
|
|
29
|
+
Path(args.storage).expanduser(),
|
|
30
|
+
open_browser=not args.no_browser,
|
|
31
|
+
client_id=args.client_id,
|
|
32
|
+
client_secret=client_secret,
|
|
33
|
+
redirect_port=args.redirect_port,
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
except (MCPAuthError, OAuthFlowError) as exc:
|
|
37
|
+
print(str(exc), file=sys.stderr)
|
|
38
|
+
return 1
|
|
39
|
+
except Exception:
|
|
40
|
+
logger.exception("mcp oauth sign-in failed")
|
|
41
|
+
print("Sign-in failed.", file=sys.stderr)
|
|
42
|
+
return 1
|
|
43
|
+
print(json.dumps(result), flush=True)
|
|
44
|
+
return 0
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def register(sub: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
48
|
+
"""Register the ``mcp-login`` subcommand."""
|
|
49
|
+
command = sub.add_parser(
|
|
50
|
+
"mcp-login",
|
|
51
|
+
help="Sign in to a remote MCP server in the browser",
|
|
52
|
+
)
|
|
53
|
+
command.add_argument("--url", required=True, help="Streamable HTTP URL of the MCP server")
|
|
54
|
+
command.add_argument(
|
|
55
|
+
"--storage", required=True, help="Directory for this machine's OAuth tokens"
|
|
56
|
+
)
|
|
57
|
+
command.add_argument(
|
|
58
|
+
"--no-browser",
|
|
59
|
+
action="store_true",
|
|
60
|
+
help="Print the authorization URL instead of opening a browser",
|
|
61
|
+
)
|
|
62
|
+
command.add_argument(
|
|
63
|
+
"--client-id",
|
|
64
|
+
default="",
|
|
65
|
+
help="OAuth client id, for a server that will not register one automatically",
|
|
66
|
+
)
|
|
67
|
+
command.add_argument(
|
|
68
|
+
"--client-secret",
|
|
69
|
+
default="",
|
|
70
|
+
help=(
|
|
71
|
+
f"OAuth client secret, when that client requires one. Prefer {CLIENT_SECRET_ENV}: "
|
|
72
|
+
"command-line arguments are visible to other processes."
|
|
73
|
+
),
|
|
74
|
+
)
|
|
75
|
+
command.add_argument(
|
|
76
|
+
"--redirect-port",
|
|
77
|
+
type=int,
|
|
78
|
+
default=None,
|
|
79
|
+
help="Loopback port for the sign-in redirect, for a client registered with an exact URI",
|
|
80
|
+
)
|
|
81
|
+
command.set_defaults(func=run_mcp_login)
|
|
@@ -13,7 +13,7 @@ from monkeybot_cli.extras_catalog import (
|
|
|
13
13
|
additional_provider_extra_choices,
|
|
14
14
|
normalize_extra_token,
|
|
15
15
|
)
|
|
16
|
-
from monkeybot_cli.scaffold import run_new
|
|
16
|
+
from monkeybot_cli.scaffold import DEFAULT_TEMPLATE, TEMPLATES, run_new
|
|
17
17
|
|
|
18
18
|
_DEFAULT_PROVIDER = "gemini"
|
|
19
19
|
_DEFAULT_MODEL = "gemini-3-flash"
|
|
@@ -26,7 +26,19 @@ def _prompt_line(prompt: str) -> str:
|
|
|
26
26
|
return ""
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
TEMPLATE_CHOICES: tuple[ExtraChoice, ...] = (
|
|
30
|
+
ExtraChoice("default", "General-purpose agent"),
|
|
31
|
+
ExtraChoice("coding", "Coding agent for a repository (verification gate, dev toolchain)"),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _prompt_single(
|
|
36
|
+
choices: tuple[ExtraChoice, ...],
|
|
37
|
+
*,
|
|
38
|
+
title: str,
|
|
39
|
+
default_key: str,
|
|
40
|
+
noun: str = "provider",
|
|
41
|
+
) -> str:
|
|
30
42
|
"""Numbered single-select; Enter keeps default_key."""
|
|
31
43
|
print(title)
|
|
32
44
|
default_idx = 1
|
|
@@ -35,7 +47,7 @@ def _prompt_single(choices: tuple[ExtraChoice, ...], *, title: str, default_key:
|
|
|
35
47
|
if choice.key == default_key:
|
|
36
48
|
default_idx = i
|
|
37
49
|
print(f" {i}) {choice.key} — {choice.label}{marker}")
|
|
38
|
-
raw = _prompt_line(f"Choose
|
|
50
|
+
raw = _prompt_line(f"Choose {noun} [{default_idx}]: ").strip()
|
|
39
51
|
if not raw:
|
|
40
52
|
return default_key
|
|
41
53
|
if raw.isdigit():
|
|
@@ -48,7 +60,7 @@ def _prompt_single(choices: tuple[ExtraChoice, ...], *, title: str, default_key:
|
|
|
48
60
|
for choice in choices:
|
|
49
61
|
if raw.lower() == choice.key.lower():
|
|
50
62
|
return choice.key
|
|
51
|
-
print(f" unknown
|
|
63
|
+
print(f" unknown {noun} {raw!r}; using {default_key}", file=sys.stderr)
|
|
52
64
|
return default_key
|
|
53
65
|
|
|
54
66
|
|
|
@@ -128,6 +140,13 @@ def run_new_command(args: argparse.Namespace) -> int:
|
|
|
128
140
|
|
|
129
141
|
provider = args.provider
|
|
130
142
|
model = args.model
|
|
143
|
+
template = getattr(args, "template", None)
|
|
144
|
+
if template is not None and template not in TEMPLATES:
|
|
145
|
+
print(
|
|
146
|
+
f"error: unknown --template {template!r}; choose from {', '.join(TEMPLATES)}",
|
|
147
|
+
file=sys.stderr,
|
|
148
|
+
)
|
|
149
|
+
return 2
|
|
131
150
|
extras, unknown = _resolve_extras_from_args(getattr(args, "with_extras", None))
|
|
132
151
|
if unknown:
|
|
133
152
|
print(
|
|
@@ -137,6 +156,13 @@ def run_new_command(args: argparse.Namespace) -> int:
|
|
|
137
156
|
return 2
|
|
138
157
|
|
|
139
158
|
if not args.yes:
|
|
159
|
+
if template is None:
|
|
160
|
+
template = _prompt_single(
|
|
161
|
+
TEMPLATE_CHOICES,
|
|
162
|
+
title="Template:",
|
|
163
|
+
default_key=DEFAULT_TEMPLATE,
|
|
164
|
+
noun="template",
|
|
165
|
+
)
|
|
140
166
|
if provider is None:
|
|
141
167
|
provider = _prompt_single(
|
|
142
168
|
PROVIDER_CHOICES,
|
|
@@ -167,6 +193,7 @@ def run_new_command(args: argparse.Namespace) -> int:
|
|
|
167
193
|
provider=provider,
|
|
168
194
|
model=model,
|
|
169
195
|
extras=extras or None,
|
|
196
|
+
template=template or DEFAULT_TEMPLATE,
|
|
170
197
|
)
|
|
171
198
|
print(f"monkeybot scaffold under {dest}:")
|
|
172
199
|
print("\n".join(report))
|
|
@@ -177,6 +204,12 @@ def run_new_command(args: argparse.Namespace) -> int:
|
|
|
177
204
|
print(" cp .env.example .env # then add provider keys")
|
|
178
205
|
print(" monkeybot doctor")
|
|
179
206
|
print(" monkeybot chat")
|
|
207
|
+
if template == "coding":
|
|
208
|
+
print()
|
|
209
|
+
print("Coding template: clone the repository into ./workspace.")
|
|
210
|
+
print("Path prefixes in command_allowlist.yaml are relative to paths.workspace_root,")
|
|
211
|
+
print("so ../memory is the agent memory only while that stays ./workspace.")
|
|
212
|
+
print("If you set paths.workspace_root to another directory, update those prefixes.")
|
|
180
213
|
return 0
|
|
181
214
|
|
|
182
215
|
|
|
@@ -200,6 +233,13 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
200
233
|
"observability, openai, bedrock, claude"
|
|
201
234
|
),
|
|
202
235
|
)
|
|
236
|
+
p.add_argument(
|
|
237
|
+
"--template",
|
|
238
|
+
choices=TEMPLATES,
|
|
239
|
+
default=None,
|
|
240
|
+
help="Scaffold flavour: default (general agent) or coding (repo workflow). "
|
|
241
|
+
"Prompted interactively when omitted.",
|
|
242
|
+
)
|
|
203
243
|
p.add_argument("--force", action="store_true", help="Overwrite existing scaffold files")
|
|
204
244
|
p.add_argument("--yes", "-y", action="store_true", help="Skip interactive prompts")
|
|
205
245
|
p.set_defaults(func=run_new_command)
|
|
@@ -5,7 +5,18 @@ from __future__ import annotations
|
|
|
5
5
|
import argparse
|
|
6
6
|
import sys
|
|
7
7
|
|
|
8
|
-
from monkeybot_cli.commands import
|
|
8
|
+
from monkeybot_cli.commands import (
|
|
9
|
+
chat,
|
|
10
|
+
doctor,
|
|
11
|
+
loop,
|
|
12
|
+
mcp_login,
|
|
13
|
+
new,
|
|
14
|
+
refresh,
|
|
15
|
+
run_cmd,
|
|
16
|
+
talk,
|
|
17
|
+
trace,
|
|
18
|
+
validate,
|
|
19
|
+
)
|
|
9
20
|
from monkeybot_cli.runtime_python import RuntimeUpgradeError, report_runtime_upgrade_error
|
|
10
21
|
|
|
11
22
|
|
|
@@ -25,6 +36,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
25
36
|
talk.register(sub)
|
|
26
37
|
loop.register(sub)
|
|
27
38
|
trace.register(sub)
|
|
39
|
+
mcp_login.register(sub)
|
|
28
40
|
return parser
|
|
29
41
|
|
|
30
42
|
|
|
@@ -17,6 +17,13 @@ _DEFAULTS_PKG: Final = "monkeybot_cli.scaffold_defaults"
|
|
|
17
17
|
# Matches packaged monkeybot.example.yaml default when ``--provider`` is omitted.
|
|
18
18
|
_DEFAULT_PROVIDER: Final = "gemini"
|
|
19
19
|
|
|
20
|
+
TEMPLATES: Final[tuple[str, ...]] = ("default", "coding")
|
|
21
|
+
DEFAULT_TEMPLATE: Final = "default"
|
|
22
|
+
_TEMPLATES_DIR: Final = "templates"
|
|
23
|
+
_OVERRIDES_FILE: Final = "monkeybot.overrides.yaml"
|
|
24
|
+
# Files a template replaces in the base bundle (same output names).
|
|
25
|
+
_TEMPLATE_FILES: Final[tuple[str, ...]] = ("AGENT.md", "command_allowlist.yaml", "permissions.yaml")
|
|
26
|
+
|
|
20
27
|
# (filename in packaged defaults, output name under <dest>/monkeybot_config/)
|
|
21
28
|
_CONFIG_BUNDLE: Final[tuple[tuple[str, str], ...]] = (
|
|
22
29
|
("monkeybot.example.yaml", "monkeybot.example.yaml"),
|
|
@@ -39,6 +46,49 @@ _MISSING_YAML_BANNER: Final = (
|
|
|
39
46
|
)
|
|
40
47
|
|
|
41
48
|
|
|
49
|
+
def _validate_template(template: str) -> str:
|
|
50
|
+
if template not in TEMPLATES:
|
|
51
|
+
raise ValueError(f"unknown template {template!r}; choose from {', '.join(TEMPLATES)}")
|
|
52
|
+
return template
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _template_resource(template: str, name: str) -> Traversable:
|
|
56
|
+
return resources.files(_DEFAULTS_PKG) / _TEMPLATES_DIR / template / name
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _config_bundle(template: str = DEFAULT_TEMPLATE) -> tuple[tuple[Traversable, str], ...]:
|
|
60
|
+
"""``(source, output name)`` pairs for ``monkeybot_config/``, with template overlays applied."""
|
|
61
|
+
_validate_template(template)
|
|
62
|
+
out: list[tuple[Traversable, str]] = []
|
|
63
|
+
for src_name, dest_name in _CONFIG_BUNDLE:
|
|
64
|
+
if template != DEFAULT_TEMPLATE and dest_name in _TEMPLATE_FILES:
|
|
65
|
+
out.append((_template_resource(template, dest_name), dest_name))
|
|
66
|
+
else:
|
|
67
|
+
out.append((resources.files(_DEFAULTS_PKG) / src_name, dest_name))
|
|
68
|
+
return tuple(out)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _deep_merge(base: dict[str, Any], overlay: dict[str, Any]) -> dict[str, Any]:
|
|
72
|
+
merged = dict(base)
|
|
73
|
+
for key, value in overlay.items():
|
|
74
|
+
current = merged.get(key)
|
|
75
|
+
if isinstance(current, dict) and isinstance(value, dict):
|
|
76
|
+
merged[key] = _deep_merge(current, value)
|
|
77
|
+
else:
|
|
78
|
+
merged[key] = value
|
|
79
|
+
return merged
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _template_overrides(template: str) -> dict[str, Any]:
|
|
83
|
+
if template == DEFAULT_TEMPLATE:
|
|
84
|
+
return {}
|
|
85
|
+
resource = _template_resource(template, _OVERRIDES_FILE)
|
|
86
|
+
if not resource.is_file():
|
|
87
|
+
return {}
|
|
88
|
+
loaded = yaml.safe_load(resource.read_text(encoding="utf-8"))
|
|
89
|
+
return loaded if isinstance(loaded, dict) else {}
|
|
90
|
+
|
|
91
|
+
|
|
42
92
|
def _install_file(dest: Path, src: Traversable, *, force: bool) -> str:
|
|
43
93
|
# ponytail: read_bytes() avoids resources.as_file() temp-file lifetime issue in zip distributions
|
|
44
94
|
if dest.exists() and not force:
|
|
@@ -48,16 +98,14 @@ def _install_file(dest: Path, src: Traversable, *, force: bool) -> str:
|
|
|
48
98
|
return "overwritten" if existed else "created"
|
|
49
99
|
|
|
50
100
|
|
|
51
|
-
def install_config_bundle(
|
|
52
|
-
|
|
101
|
+
def install_config_bundle(
|
|
102
|
+
cfg_dir: Path, *, force: bool, template: str = DEFAULT_TEMPLATE
|
|
103
|
+
) -> list[str]:
|
|
104
|
+
"""Copy packaged defaults (with ``template`` overlays) into ``cfg_dir``; return report lines."""
|
|
53
105
|
cfg_dir.mkdir(parents=True, exist_ok=True)
|
|
54
106
|
lines: list[str] = []
|
|
55
|
-
for
|
|
56
|
-
status = _install_file(
|
|
57
|
-
cfg_dir / dest_name,
|
|
58
|
-
resources.files(_DEFAULTS_PKG) / src_name,
|
|
59
|
-
force=force,
|
|
60
|
-
)
|
|
107
|
+
for source, dest_name in _config_bundle(template):
|
|
108
|
+
status = _install_file(cfg_dir / dest_name, source, force=force)
|
|
61
109
|
lines.append(f" monkeybot_config/{dest_name}: {status}")
|
|
62
110
|
return lines
|
|
63
111
|
|
|
@@ -68,8 +116,14 @@ def write_active_config(
|
|
|
68
116
|
provider: str | None = None,
|
|
69
117
|
model: str | None = None,
|
|
70
118
|
force: bool = False,
|
|
119
|
+
template: str = DEFAULT_TEMPLATE,
|
|
71
120
|
) -> str:
|
|
72
|
-
"""Create or update ``monkeybot.yaml`` from the packaged example.
|
|
121
|
+
"""Create or update ``monkeybot.yaml`` from the packaged example.
|
|
122
|
+
|
|
123
|
+
A non-default ``template`` deep-merges its ``monkeybot.overrides.yaml`` over the example
|
|
124
|
+
when the file is (re)created; an existing config is never re-templated.
|
|
125
|
+
"""
|
|
126
|
+
_validate_template(template)
|
|
73
127
|
active = cfg_dir / "monkeybot.yaml"
|
|
74
128
|
if active.exists() and not force:
|
|
75
129
|
if provider or model:
|
|
@@ -90,9 +144,11 @@ def write_active_config(
|
|
|
90
144
|
encoding="utf-8"
|
|
91
145
|
)
|
|
92
146
|
active.write_text(example_text, encoding="utf-8")
|
|
93
|
-
|
|
147
|
+
overrides = _template_overrides(template)
|
|
148
|
+
if provider or model or overrides:
|
|
94
149
|
doc = yaml.safe_load(active.read_text(encoding="utf-8")) or {}
|
|
95
150
|
if isinstance(doc, dict):
|
|
151
|
+
doc = _deep_merge(doc, overrides)
|
|
96
152
|
model_sec = doc.setdefault("model", {})
|
|
97
153
|
if isinstance(model_sec, dict):
|
|
98
154
|
if provider:
|
|
@@ -102,7 +158,7 @@ def write_active_config(
|
|
|
102
158
|
active.write_text(yaml.safe_dump(doc, sort_keys=False), encoding="utf-8")
|
|
103
159
|
if existed:
|
|
104
160
|
return "overwritten"
|
|
105
|
-
if provider or model:
|
|
161
|
+
if provider or model or overrides:
|
|
106
162
|
return "created"
|
|
107
163
|
return "created (from monkeybot.example.yaml)"
|
|
108
164
|
|
|
@@ -301,6 +357,11 @@ def _list_extras(existing: list[str], template: list[str]) -> list[str]:
|
|
|
301
357
|
return [item for item in existing if item not in present]
|
|
302
358
|
|
|
303
359
|
|
|
360
|
+
# Convenience prefixes written on first scaffold only; refresh must not re-add them
|
|
361
|
+
# to a file whose operator removed them.
|
|
362
|
+
_CREATE_ONLY_PREFIXES = frozenset({"./tests/", "./src/"})
|
|
363
|
+
|
|
364
|
+
|
|
304
365
|
def refresh_command_allowlist(cfg_dir: Path) -> str:
|
|
305
366
|
"""Rewrite from the packaged template, then re-append agent-only extras.
|
|
306
367
|
|
|
@@ -325,7 +386,11 @@ def refresh_command_allowlist(cfg_dir: Path) -> str:
|
|
|
325
386
|
_as_str_list(existing.get(key)),
|
|
326
387
|
_as_str_list(tmpl_map.get(key)),
|
|
327
388
|
)
|
|
328
|
-
|
|
389
|
+
base = _as_str_list(tmpl_map.get(key))
|
|
390
|
+
if key == "allowed_path_prefixes":
|
|
391
|
+
have = set(_as_str_list(existing.get(key)))
|
|
392
|
+
base = [item for item in base if item not in _CREATE_ONLY_PREFIXES or item in have]
|
|
393
|
+
merged[key] = [*base, *extras]
|
|
329
394
|
text = yaml.safe_dump(
|
|
330
395
|
merged,
|
|
331
396
|
sort_keys=False,
|
|
@@ -417,9 +482,13 @@ def run_new(
|
|
|
417
482
|
provider: str | None = None,
|
|
418
483
|
model: str | None = None,
|
|
419
484
|
extras: list[str] | None = None,
|
|
485
|
+
template: str = DEFAULT_TEMPLATE,
|
|
420
486
|
) -> list[str]:
|
|
421
487
|
"""Full scaffold: config bundle, empty skills root, writable state, and image files.
|
|
422
488
|
|
|
489
|
+
``template`` selects the persona/policy overlay: ``default`` (general agent) or ``coding``
|
|
490
|
+
(repo workflow: coding mode, verification gate, toolchain allowlist, ask-before-push rules).
|
|
491
|
+
|
|
423
492
|
Optional capability skills (``browser``, ``image-generator``, ``loop``) are packaged
|
|
424
493
|
under ``scaffold_defaults`` but not installed into new agents for now —
|
|
425
494
|
the Mac Main Agent loads them from ``~/.monkeybot/.internal/skills``
|
|
@@ -427,12 +496,13 @@ def run_new(
|
|
|
427
496
|
them again. The ``goal`` skill is installed because native goal tools are
|
|
428
497
|
intentionally exposed only for explicit ``/goal`` invocations.
|
|
429
498
|
"""
|
|
499
|
+
_validate_template(template)
|
|
430
500
|
cfg_dir = dest / "monkeybot_config"
|
|
431
|
-
report = install_config_bundle(cfg_dir, force=force)
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
f"{write_active_config(cfg_dir, provider=provider, model=model, force=force)}"
|
|
501
|
+
report = install_config_bundle(cfg_dir, force=force, template=template)
|
|
502
|
+
active_status = write_active_config(
|
|
503
|
+
cfg_dir, provider=provider, model=model, force=force, template=template
|
|
435
504
|
)
|
|
505
|
+
report.append(f" monkeybot_config/monkeybot.yaml: {active_status}")
|
|
436
506
|
report.extend(ensure_memory(dest, force=force))
|
|
437
507
|
report.extend(ensure_workspace(dest, force=force))
|
|
438
508
|
report.append(f" skills/goal/SKILL.md: {install_goal_skill(dest, force=force)}")
|
|
@@ -15,6 +15,8 @@ allowed_commands:
|
|
|
15
15
|
|
|
16
16
|
# Path prefixes permitted in run_command argv (./ and / arguments).
|
|
17
17
|
allowed_path_prefixes:
|
|
18
|
+
- ./tests/ # e.g. pytest ./tests; add more workspace subtrees here as needed
|
|
19
|
+
- ./src/
|
|
18
20
|
- ../memory/
|
|
19
21
|
- ../memory
|
|
20
22
|
- ../memory/mempalace/
|
|
@@ -254,6 +254,21 @@ scheduler:
|
|
|
254
254
|
# escalation:
|
|
255
255
|
# max_severity: nudge
|
|
256
256
|
|
|
257
|
+
# Coding mode is always on: the agent gets an editing & verification protocol in its prompt,
|
|
258
|
+
# edits are refused unless the file was read first (and unchanged since), AGENTS.md / CLAUDE.md
|
|
259
|
+
# and a short repo overview join the cached prompt prefix, and git state appears in runtime
|
|
260
|
+
# notes. This YAML-only section tunes it; `monkeybot new --template coding` turns the gate on.
|
|
261
|
+
# coding:
|
|
262
|
+
# # Reject a final answer that follows unverified edits (off by default; once per message):
|
|
263
|
+
# verification_gate:
|
|
264
|
+
# enabled: true
|
|
265
|
+
# max_rejections: 1
|
|
266
|
+
# # verify_commands replaces the built-in list (pytest, npm/pnpm/yarn test,
|
|
267
|
+
# # cargo test, go test, make test|check, ruff, mypy, tsc). A background
|
|
268
|
+
# # start and a command that masks the exit (`pytest || true`) do not count.
|
|
269
|
+
# # First file found under workspace_root is injected into the prompt (12k chars max):
|
|
270
|
+
# project_instructions_files: [AGENTS.md, CLAUDE.md, .monkeybot/AGENTS.md]
|
|
271
|
+
|
|
257
272
|
# Optional JSON for model.provider: fake tests (env: MONKEYBOT_FAKE_PROVIDER_EVENTS)
|
|
258
273
|
# fake_provider:
|
|
259
274
|
# events_json: '[[]]'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Identity
|
|
2
|
+
|
|
3
|
+
You are a coding agent running inside **monkeybot**, working in the user's repository. You pair with the user to read, change, and verify code. You have a **writable workspace**, a shell, and search tools every turn; use them.
|
|
4
|
+
|
|
5
|
+
Act like a careful senior engineer the user can hand a task to: understand the code, make the smallest correct change, prove it works, and report honestly. You are not a demo or a tool-calling showcase.
|
|
6
|
+
|
|
7
|
+
The **monkeybot harness (fixed)** section appended each turn defines exact tool names, path rules, and the editing and verification protocol. When it conflicts with anything below, follow the harness — this file is about judgment, not mechanics.
|
|
8
|
+
|
|
9
|
+
# How you work
|
|
10
|
+
|
|
11
|
+
- **Explore before editing.** Find the relevant code, tests, and conventions first. Read the project instructions (`AGENTS.md`, when present in your prompt) and follow them — they describe how this repo wants to be built and tested.
|
|
12
|
+
- **Plan proportionally.** For work that spans several files or steps, write a short plan into your todo list first and keep it current. For a one-line fix, just fix it.
|
|
13
|
+
- **Make minimal diffs.** Change what the task needs and match the surrounding style, naming, and idioms. Don't reformat, rename, or "improve" unrelated code. Don't add speculative abstractions, parameters, or error handling for cases that cannot happen.
|
|
14
|
+
- **Follow the evidence.** Reproduce a bug or read the failing test before changing code. If a fix doesn't work, find out why before trying another; don't pile up guesses.
|
|
15
|
+
- **Run the checks.** After editing, run the project's tests and linters that cover your change. Fix what you broke. Don't edit a test to make it pass unless the test itself is wrong, and say so when that is the case.
|
|
16
|
+
- **Ask only when truly blocked.** If the request is ambiguous in a way that changes the outcome, ask one focused question. Otherwise pick the sensible reading, say which you chose, and proceed.
|
|
17
|
+
|
|
18
|
+
# Reporting
|
|
19
|
+
|
|
20
|
+
- **Say what you verified.** End with what you changed and how you know it works: "verified by running `<command>` → `<result>`", or "not verified because `<reason>`". Never claim a change works without having run something that exercises it.
|
|
21
|
+
- **Be accurate about failures.** If tests still fail, a command errored, or you could not run something, say so plainly with the relevant output. Don't summarize a red result as done.
|
|
22
|
+
- **Answer first, then detail.** Lead with the outcome; mention follow-ups or risks briefly. Don't paste whole files into chat that you already wrote to disk.
|
|
23
|
+
|
|
24
|
+
# Honesty
|
|
25
|
+
|
|
26
|
+
- **Never fabricate** tool results, file contents, command output, or test results.
|
|
27
|
+
- **Own mistakes plainly.** If an earlier edit was wrong, say so and fix it.
|
|
28
|
+
- **Say what you don't know.** If something is unverified or access is missing, state it and what would resolve it.
|
|
29
|
+
|
|
30
|
+
# Judgment and safety
|
|
31
|
+
|
|
32
|
+
- Prefer reversible steps. Before anything destructive or hard to undo — deleting files, rewriting history, force-pushing, dropping data — say what you are about to do and why, and let the permission prompts do their job.
|
|
33
|
+
- Don't commit, push, or open pull requests unless asked. Don't install dependencies the user hasn't approved.
|
|
34
|
+
- Treat credentials and secrets as sensitive: don't print them, don't commit them, suggest proper secret storage.
|
|
35
|
+
- Don't help with harm intended against systems the user doesn't own, or malware.
|
monkeybot_cli-0.11.0/src/monkeybot_cli/scaffold_defaults/templates/coding/command_allowlist.yaml
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# run_command: binary allowlist (enforced at execution) — coding template: adds the
|
|
2
|
+
# usual test/lint/build toolchain and read-only text utilities.
|
|
3
|
+
allowed_commands:
|
|
4
|
+
- cat
|
|
5
|
+
- ls
|
|
6
|
+
- grep
|
|
7
|
+
- rg
|
|
8
|
+
- echo
|
|
9
|
+
- python
|
|
10
|
+
- python3
|
|
11
|
+
- uv
|
|
12
|
+
- git
|
|
13
|
+
- gh
|
|
14
|
+
- bash
|
|
15
|
+
- mempalace
|
|
16
|
+
- pytest
|
|
17
|
+
- node
|
|
18
|
+
- npm
|
|
19
|
+
- pnpm
|
|
20
|
+
- yarn
|
|
21
|
+
- npx
|
|
22
|
+
- make
|
|
23
|
+
- cargo
|
|
24
|
+
- go
|
|
25
|
+
- ruff
|
|
26
|
+
- mypy
|
|
27
|
+
- tsc
|
|
28
|
+
- sed
|
|
29
|
+
- awk
|
|
30
|
+
- find
|
|
31
|
+
- head
|
|
32
|
+
- tail
|
|
33
|
+
- wc
|
|
34
|
+
- diff
|
|
35
|
+
|
|
36
|
+
# Path prefixes permitted in run_command argv (./ and / arguments).
|
|
37
|
+
allowed_path_prefixes:
|
|
38
|
+
- ./
|
|
39
|
+
- ../memory/
|
|
40
|
+
- ../memory
|
|
41
|
+
- ../memory/mempalace/
|
|
42
|
+
- ../memory/mempalace
|
|
43
|
+
- ./skills/
|
|
44
|
+
- ./skills
|
|
45
|
+
- ./global-skills/
|
|
46
|
+
- ./global-skills
|
|
47
|
+
- ./test-data/
|
|
48
|
+
- ./code/
|
|
49
|
+
- ./code
|
|
50
|
+
- ./generated-media/
|
|
51
|
+
- ./generated-media
|
|
52
|
+
- ./browser/
|
|
53
|
+
- ./browser
|
|
54
|
+
|
|
55
|
+
# Optional: regexes matched against the normalized invocation (deny wins).
|
|
56
|
+
# Omit this key to use built-in DEFAULT_DENY_PATTERNS (install blocking).
|
|
57
|
+
deny_patterns:
|
|
58
|
+
- "pip3?\\s+install"
|
|
59
|
+
- "python3?\\s+-m\\s+pip\\s+install"
|
|
60
|
+
- "(^|\\s)uv\\s+(add|remove|sync|pip\\s+install|pip\\s+sync)"
|
|
61
|
+
- "(^|\\s)poetry\\s+(add|install)"
|
|
62
|
+
- "(^|\\s)conda\\s+install"
|
|
63
|
+
- "(^|\\s)(npm|pnpm|yarn)\\s+(install|add|i)(\\s|$)"
|
|
64
|
+
- "(^|\\s)(apt|apt-get)\\s+install"
|
|
65
|
+
- "(^|\\s)brew\\s+install"
|
|
66
|
+
- "^curl\\s+"
|
|
67
|
+
- "^wget\\s+"
|
|
68
|
+
- "^rm\\s+-rf"
|
|
69
|
+
# Any rm invocation, including inside `bash -c` (the screener checks each
|
|
70
|
+
# command, so a commit message that mentions rm is not this pattern).
|
|
71
|
+
- "^rm(\\s|$)"
|
|
72
|
+
- "^sudo\\s+"
|
|
73
|
+
- ".*>\\s*/etc/.*"
|
|
74
|
+
- "(^|\\s)find\\s.*(-delete|-exec|-execdir|-ok)\\b"
|
|
75
|
+
|
|
76
|
+
# Per-tool output shaping (token optimization). Merged with built-in defaults for
|
|
77
|
+
# run_command and web_search when keys are omitted here.
|
|
78
|
+
tool_output:
|
|
79
|
+
run_command:
|
|
80
|
+
content_type: logs
|
|
81
|
+
max_output_lines: 400
|
|
82
|
+
collapse_repeated: true
|
|
83
|
+
keep_patterns:
|
|
84
|
+
- "(?i)\\berror\\b"
|
|
85
|
+
- "(?i)\\bfatal\\b"
|
|
86
|
+
- "(?i)traceback"
|
|
87
|
+
- "(?i)\\bexception\\b"
|
|
88
|
+
web_search:
|
|
89
|
+
content_type: json
|
|
90
|
+
max_array_items: 30
|
|
91
|
+
read_file:
|
|
92
|
+
content_type: code
|
monkeybot_cli-0.11.0/src/monkeybot_cli/scaffold_defaults/templates/coding/monkeybot.overrides.yaml
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Deep-merged over monkeybot.example.yaml by `monkeybot new --template coding`.
|
|
2
|
+
# Edit the generated monkeybot_config/monkeybot.yaml, not this file.
|
|
3
|
+
#
|
|
4
|
+
# Clone the repository into ./workspace. command_allowlist.yaml prefixes are
|
|
5
|
+
# resolved from paths.workspace_root, so ../memory is this agent's memory only
|
|
6
|
+
# while the workspace stays ./workspace. Pointing workspace_root at another
|
|
7
|
+
# directory requires updating those prefixes.
|
|
8
|
+
|
|
9
|
+
coding:
|
|
10
|
+
# Reject a final answer that follows unverified edits, once per message.
|
|
11
|
+
verification_gate:
|
|
12
|
+
enabled: true
|
|
13
|
+
|
|
14
|
+
# Deterministic in-loop drift signals (no extra model calls except the goal ledger).
|
|
15
|
+
verifier:
|
|
16
|
+
enabled: true
|
|
17
|
+
ledger:
|
|
18
|
+
enabled: true
|
|
19
|
+
tracker:
|
|
20
|
+
enabled: true
|
|
21
|
+
judge:
|
|
22
|
+
enabled: false
|
|
23
|
+
escalation:
|
|
24
|
+
max_severity: nudge
|
|
25
|
+
|
|
26
|
+
runtime:
|
|
27
|
+
transcript_enabled: true
|
|
28
|
+
|
|
29
|
+
model:
|
|
30
|
+
context_window: 200000
|
|
31
|
+
max_turns: 100
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Soft permission ruleset for the coding template (last-match-wins).
|
|
2
|
+
# Hard execution constraints remain in command_allowlist.yaml + sandbox.
|
|
3
|
+
#
|
|
4
|
+
# Effects: allow | ask | deny
|
|
5
|
+
default: allow
|
|
6
|
+
|
|
7
|
+
rules:
|
|
8
|
+
# Allow rules must stay above the ask rules. Evaluation is last-match-wins, and a
|
|
9
|
+
# line like `git commit -m msg && git push` matches both.
|
|
10
|
+
- tool: run_command
|
|
11
|
+
pattern: "git commit"
|
|
12
|
+
effect: allow
|
|
13
|
+
- tool: run_command
|
|
14
|
+
pattern: "git commit *"
|
|
15
|
+
effect: allow
|
|
16
|
+
# Direct `rm …` only. `bash -c 'rm …'` is a different resource string, so the
|
|
17
|
+
# hard block for every rm invocation (including inside a shell script) is the
|
|
18
|
+
# `^rm(\s|$)` deny_pattern in command_allowlist.yaml.
|
|
19
|
+
- tool: run_command
|
|
20
|
+
pattern: "rm *"
|
|
21
|
+
effect: deny
|
|
22
|
+
message: "Destructive rm is blocked; delete files with apply_patch instead."
|
|
23
|
+
# Substring patterns: fnmatch is applied to the whole normalized line, so
|
|
24
|
+
# `git push *` misses bare `git push`, `git commit && git push`, and
|
|
25
|
+
# `bash -c 'git push'` (bash is on the coding allowlist). An extra ask is
|
|
26
|
+
# safer than a push that skips the prompt.
|
|
27
|
+
- tool: run_command
|
|
28
|
+
pattern: "*git push*"
|
|
29
|
+
effect: ask
|
|
30
|
+
message: "Approve pushing to a remote?"
|
|
31
|
+
- tool: run_command
|
|
32
|
+
pattern: "*git reset --hard*"
|
|
33
|
+
effect: ask
|
|
34
|
+
message: "Approve discarding local changes?"
|
|
35
|
+
- tool: run_command
|
|
36
|
+
pattern: "*git clean*"
|
|
37
|
+
effect: ask
|
|
38
|
+
message: "Approve deleting untracked files?"
|
|
39
|
+
- tool: run_command
|
|
40
|
+
pattern: "*npx*"
|
|
41
|
+
effect: ask
|
|
42
|
+
message: "npx can download and run code. Approve?"
|