Config policies (16 files): alert_routing, architecture_pressure, backlog, cost_weights, data_governance, incident_escalation, incident_intelligence, network_allowlist, nodes_registry, observability_sources, rbac_tools_matrix, release_gate, risk_attribution, risk_policy, slo_policy, tool_limits, tools_rollout Ops (22 files): Caddyfile, calendar compose, grafana voice dashboard, deployments/incidents logs, runbooks for alerts/audit/backlog/incidents/sofiia/voice, cron jobs, scripts (alert_triage, audit_cleanup, migrate_*, governance, schedule), task_registry, voice alerts/ha/latency/policy Docs (30+ files): HUMANIZED_STEPAN v2.7-v3 changelogs and runbooks, NODA1/NODA2 status and setup, audit index and traces, backlog, incident, supervisor, tools, voice, opencode, release, risk, aistalk, spacebot Made-with: Cursor
266 lines
5.6 KiB
Markdown
266 lines
5.6 KiB
Markdown
# RepoTool - Read-only Repository Access
|
|
|
|
## Overview
|
|
|
|
RepoTool provides read-only access to the DAARION repository filesystem for agents (primarily Sofiia). It allows viewing code, configs, and searching through the codebase without any write or execute capabilities.
|
|
|
|
## Integration
|
|
|
|
### Tool Definition
|
|
|
|
RepoTool is registered in `services/router/tool_manager.py` under `TOOL_DEFINITIONS`:
|
|
|
|
```python
|
|
{
|
|
"type": "function",
|
|
"function": {
|
|
"name": "repo_tool",
|
|
"description": "📂 Read-only доступ до файловї системи репозиторію...",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"action": {
|
|
"type": "string",
|
|
"enum": ["tree", "read", "search", "metadata"]
|
|
},
|
|
"path": {"type": "string"},
|
|
"start_line": {"type": "integer"},
|
|
"end_line": {"type": "integer"},
|
|
"depth": {"type": "integer"},
|
|
"glob": {"type": "string"},
|
|
"query": {"type": "string"},
|
|
"limit": {"type": "integer"},
|
|
"max_bytes": {"type": "integer"}
|
|
},
|
|
"required": ["action"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### RBAC Configuration
|
|
|
|
Added to `services/router/agent_tools_config.py` in `FULL_STANDARD_STACK` - available to all agents.
|
|
|
|
## Actions
|
|
|
|
### 1. tree - Directory Structure
|
|
|
|
Show directory tree starting from a path.
|
|
|
|
**Parameters:**
|
|
- `path`: Starting path (default: ".")
|
|
- `depth`: Maximum depth (default: 3, max: 10)
|
|
- `glob`: Optional glob pattern to filter files
|
|
|
|
**Example:**
|
|
```json
|
|
{
|
|
"action": "tree",
|
|
"path": "services",
|
|
"depth": 2
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"result": {
|
|
"tree": {
|
|
"router": {"main.py": "[file]", "tool_manager.py": "[file]"},
|
|
"gateway": {"main.py": "[file]"}
|
|
},
|
|
"path": "services"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. read - File Content
|
|
|
|
Read file contents with optional line limits.
|
|
|
|
**Parameters:**
|
|
- `path`: File path (required)
|
|
- `start_line`: Starting line (default: 1)
|
|
- `end_line`: Ending line (optional)
|
|
- `max_bytes`: Max bytes to read (default: 200KB, max: 1MB)
|
|
|
|
**Example:**
|
|
```json
|
|
{
|
|
"action": "read",
|
|
"path": "services/router/main.py",
|
|
"start_line": 1,
|
|
"end_line": 50
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"result": {
|
|
"path": "services/router/main.py",
|
|
"content": "import asyncio\n...",
|
|
"lines": 50,
|
|
"start_line": 1,
|
|
"end_line": 50
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. search - Text Search
|
|
|
|
Search for text in files using grep.
|
|
|
|
**Parameters:**
|
|
- `query`: Search query (required)
|
|
- `path`: Starting path (default: ".")
|
|
- `glob`: File pattern (e.g., "**/*.py")
|
|
- `limit`: Max results (default: 50, max: 200)
|
|
|
|
**Example:**
|
|
```json
|
|
{
|
|
"action": "search",
|
|
"query": "async def",
|
|
"path": "services",
|
|
"glob": "**/*.py",
|
|
"limit": 20
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"result": {
|
|
"query": "async def",
|
|
"path": "services",
|
|
"matches": [
|
|
{"file": "router/main.py", "line": "45", "content": "async def handle_request"},
|
|
{"file": "router/main.py", "line": "102", "content": "async def process_message"}
|
|
],
|
|
"count": 2
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. metadata - Git Information
|
|
|
|
Get git repository metadata.
|
|
|
|
**Parameters:**
|
|
- `path`: Path within repo (optional)
|
|
|
|
**Example:**
|
|
```json
|
|
{
|
|
"action": "metadata",
|
|
"path": "."
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"result": {
|
|
"path": ".",
|
|
"repo_root": "/path/to/repo",
|
|
"commit": "abc123def456",
|
|
"branch": "main",
|
|
"dirty": false
|
|
}
|
|
}
|
|
```
|
|
|
|
## Security Features
|
|
|
|
### Path Traversal Protection
|
|
- Blocks `..` in paths
|
|
- Rejects absolute paths outside repo root
|
|
- Validates resolved path stays within repo root
|
|
|
|
### Symlink Escape Prevention
|
|
- Uses `os.path.realpath()` to resolve symlinks
|
|
- Ensures resolved path is still within repo root
|
|
- Blocks access through symlinks to external locations
|
|
|
|
### Secret Masking
|
|
Files and content containing secrets are automatically masked:
|
|
|
|
**Masked file patterns:**
|
|
- `.env`, `.env.local`, `.env.production`
|
|
- `*secrets*`, `*credentials*`, `*keys*`, `*tokens*`, `*passwords*`
|
|
|
|
**Masked content patterns:**
|
|
```
|
|
api_key = xxx → api_key = ***
|
|
token = xxx → token = ***
|
|
password = xxx → password = ***
|
|
SECRET_KEY=xxx → SECRET_KEY=***
|
|
Bearer xxx → Bearer ***
|
|
-----BEGIN PRIVATE KEY----- → [MASKED]
|
|
```
|
|
|
|
### Limits
|
|
| Limit | Default | Max |
|
|
|-------|---------|-----|
|
|
| Tree depth | 3 | 10 |
|
|
| Search results | 50 | 200 |
|
|
| File size | 200KB | 1MB |
|
|
| Lines per read | 1000 | - |
|
|
| Search timeout | - | 10s |
|
|
|
|
## Example Usage
|
|
|
|
### Sofiia Commands
|
|
|
|
```
|
|
"Покажи структуру папки services"
|
|
"Прочитай файл services/router/main.py перші 50 рядків"
|
|
"Знайди всі файли з 'async def' в папці services"
|
|
"Який останній коміт?"
|
|
```
|
|
|
|
## Error Responses
|
|
|
|
```json
|
|
{
|
|
"success": false,
|
|
"result": null,
|
|
"error": "Path traversal detected. Access denied."
|
|
}
|
|
```
|
|
|
|
```json
|
|
{
|
|
"success": false,
|
|
"result": null,
|
|
"error": "File too large: 500000 bytes (max: 204800)"
|
|
}
|
|
```
|
|
|
|
## Testing
|
|
|
|
Run tests:
|
|
```bash
|
|
cd /path/to/repo
|
|
pytest tools/repo_tool/tests/test_repo_tool.py -v
|
|
```
|
|
|
|
Test coverage:
|
|
- Path traversal blocked
|
|
- Symlink escape blocked
|
|
- Absolute path blocked
|
|
- Tree action works
|
|
- Read action works with line limits
|
|
- Search finds content
|
|
- Metadata returns git info
|
|
- Secret files (.env) masked
|
|
- Inline secrets masked
|
|
- Size limits enforced
|
|
- Depth limits enforced
|