Every agent that interacts with the codebase starts with file tools. ReadFileTool reads files. WriteFileTool creates or overwrites them. ListFilesTool shows what's there. Simple operations, but the security model around them is what makes agents safe.
All tools live in app/baseKRIZAN/Agents/Tools/ — framework code, not addon code. Any agent in any module uses the same tools with the same security guarantees.
ToolInterface: The Contract
interface ToolInterface
{
public function getName(): string;
public function getDescription(): string;
public function getInputSchema(): array;
public function execute(array $params): array;
}
Four methods. getName() returns the tool identifier the AI uses in tool_use blocks. getDescription() gives the AI context about when to use the tool. getInputSchema() returns JSON Schema for parameters. execute() does the work and returns ['success' => bool, 'content' => string].
ReadFileTool
class ReadFileTool implements ToolInterface
{
public function __construct(
private string $basePath,
private array $allowedPaths = [],
private array $blockedPaths = [],
private int $maxFileSize = 102400 // 100KB
) {}
}
Path Validation
Every file read goes through two checks:
1. realpath() resolution — resolves symlinks and ../ traversal:
$realPath = realpath($fullPath);
if ($realPath === false) {
return ['success' => false, 'error' => 'File not found'];
}
If an agent requests ../../etc/passwd, realpath() resolves it to /etc/passwd — which won't pass the next check.
2. str_starts_with() against base path:
if (!str_starts_with($realPath, $this->basePath)) {
return ['success' => false, 'error' => 'Path outside allowed directory'];
}
The file must be within the project root. No escaping to system files.
3. Blocked paths — additional restrictions per agent:
foreach ($this->blockedPaths as $blocked) {
if (str_starts_with($realPath, $blocked)) {
return ['success' => false, 'error' => 'Path is blocked'];
}
}
VajbCoder has app/baseKRIZAN/ in its blocked list — it can't read framework internals. TestAgent has no blocked paths — it needs to read everything to write effective tests.
Line Range Support
'input_schema' => [
'properties' => [
'file' => ['type' => 'string'],
'start_line' => ['type' => 'integer'],
'end_line' => ['type' => 'integer'],
],
'required' => ['file'],
]
Agents can request specific line ranges — useful when they already know which part of a file they need. Reduces context window usage.
WriteFileTool
Same constructor pattern (basePath, allowedPaths, blockedPaths, maxFileSize). Additional security: allowed paths are checked positively — the file must be in at least one allowed path.
Atomic Writes
$tempFile = $fullPath . '.tmp.' . bin2hex(random_bytes(4));
file_put_contents($tempFile, $content);
rename($tempFile, $fullPath);
Write to temp file, then rename(). This is atomic on most filesystems — the file either has the old content or the new content, never a partially-written state. If the write fails (disk full, permissions), the original file is untouched.
Agent-Specific Boundaries
// VajbCoder can write to:
$allowedPaths = ['moduli/', 'app/Controllers/', 'app/Models/', 'app/Jobs/'];
// TestAgent can only write to:
$allowedPaths = ['tests/'];
// ReviewAgent has NO WriteFileTool (read-only agent)
The boundaries are set in agent constructors and enforced at the tool level. Even if an AI generates a write_file call for app/baseKRIZAN/Bootstrap.php, the tool rejects it before touching the filesystem.
ListFilesTool
public function execute(array $params): array
{
$path = $params['path'] ?? '.';
$pattern = $params['pattern'] ?? '*';
$maxDepth = min($params['depth'] ?? 3, 5); // Capped at 5
// ...
}
Glob-based directory listing. Depth is capped at 5 levels to prevent agents from requesting the entire filesystem tree. The tool returns file names, sizes, and types — enough for an agent to decide what to read next.
Why baseKRIZAN, Not LIMAgents
Tools used to live in app/addons/LIMAgents/Agents/Tools/. They were moved to app/baseKRIZAN/Agents/Tools/ during the refactoring. The reason: module-specific agents (PMAgent in the PM module, FiskAgent in fiskalizacija) also need file tools. If tools live in LIMAgents, every module depends on LIMAgents. If tools live in baseKRIZAN, they're framework utilities — available to any agent regardless of which addon it belongs to.
DelegateAgentTool, ListAgentsTool, and other orchestration tools also moved to baseKRIZAN for the same reason.
Up Next
Next up: GrepCodebaseTool and DatabaseSchemaTool: How Agents Understand the Codebase — regex search with file type filters, result truncation at 6K chars, and database introspection that gives agents the schema context they need to write correct SQL.
Comments (0)
No comments yet. Be the first to share your thoughts!
Leave a Comment