4.2 KiB
Tool placement policy (T-20)
Where a piece of agent-callable capability lives is decided by how many vendor integrations its domain logic touches, not by convenience or file size. This is the decision rule referenced from ARCHITECTURE.md.
Decision rule
Ask: does this tool's purpose depend on a specific external vendor or
SaaS backend (an integrations/<vendor>/ package)?
- Single vendor →
integrations/<vendor>/tools/. The tool only makes sense in terms of one vendor's domain (a Datadog query, a GitHub mutation, a Sentry issue lookup). This is the default, most common case — see "Adding a Tool" inAGENTS.md. - No vendor at all →
tools/system/. The tool's domain purpose has nothing to do with an external vendor — local process introspection, sandboxed code execution, RAG-style guidance retrieval. An incidental import of a vendor client for a side concern (e.g. resolving one of several possible credential sources, or picking a notification channel for delivery) does not make a tool vendor-specific; the test is the tool's reason to exist, not every import in the file. - Genuinely spans 2+ vendor integrations →
tools/cross_vendor/. The tool's logic itself correlates or orchestrates across multipleintegrations/<vendor>/packages — e.g.fix_sentry_issuereads fromintegrations.sentryand hands the fix tointegrations.pi. This is the narrow bucket; don't reach for it just because a tool happens to format its output for a second vendor (e.g. "Slack-ready" report text is still single-vendor logic, not cross-vendor). - True surface-level (CLI + REPL) duplication, not tool logic at all
→
surfaces/shared/(see T-21). This is a different axis entirely — it's about presentation code two surfaces both need, not about where an agent-callable tool's business logic lives.
tools/ also holds framework subsystems that aren't individual tools —
tools/investigation (the investigation pipeline), tools/interactive_shell
(REPL action tools), tools/registry.py (the tool registry itself). These
stay at the top level of tools/; the system / cross_vendor split only
applies to individual tool packages.
Current state (as of T-19)
Applied to the pre-existing top-level tools/ packages:
| Package | Placement | Why |
|---|---|---|
tools/system/fleet_monitoring/ |
system | Local AI-agent fleet monitoring; no vendor. |
tools/system/python_execution_tool/ |
system | Generic sandboxed Python execution; the GitHub token import is one of several optional credential sources, not the tool's purpose. |
tools/system/sre_guidance_tool/ |
system | Local knowledge-base retrieval; no vendor. |
tools/system/watch_dog/ |
system | CLI/REPL process monitoring; Telegram is only the alarm-delivery channel, not the tool's domain. |
tools/cross_vendor/fix_sentry_issue/ |
cross_vendor | Reads a Sentry issue and hands the fix to the Pi coding agent — two integrations/ packages in one tool's logic. |
Left as-is, not yet migrated (single-vendor tools that predate the
vendor-first tool layout and belong under integrations/<vendor>/tools/,
not tools/system/ or tools/cross_vendor/):
tools/community_followup_tool/,tools/git_deploy_timeline_tool/,tools/work_status_report_tool/— GitHub-only.tools/slack_send_message_tool/— Slack-only.tools/pi_coding_tool/— Pi-only.
Migrating these to integrations/<vendor>/tools/ is in scope for T-18
(full vendor-first completion), not this policy change — moving them into
tools/system/ or tools/cross_vendor/ now would misclassify them.
Registry mechanics
tools/system/ and tools/cross_vendor/ are ordinary packages discovered
by tools/registry.py's top-level walk of tools/. Each declares a
TOOL_MODULES tuple in its __init__.py (the same manifest mechanism
tools/slack_send_message_tool/__init__.py already uses for its own
tool submodule) listing the tool packages nested one level inside it.
Adding a new system or cross-vendor tool means adding its package name to
the relevant TOOL_MODULES tuple — no registry code changes required.