feat: Add intelligent auto-router and enhanced integrations
- Add intelligent-router.sh hook for automatic agent routing - Add AUTO-TRIGGER-SUMMARY.md documentation - Add FINAL-INTEGRATION-SUMMARY.md documentation - Complete Prometheus integration (6 commands + 4 tools) - Complete Dexto integration (12 commands + 5 tools) - Enhanced Ralph with access to all agents - Fix /clawd command (removed disable-model-invocation) - Update hooks.json to v5 with intelligent routing - 291 total skills now available - All 21 commands with automatic routing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
277
dexto/packages/tools-filesystem/src/filesystem-service.test.ts
Normal file
277
dexto/packages/tools-filesystem/src/filesystem-service.test.ts
Normal file
@@ -0,0 +1,277 @@
|
||||
/**
|
||||
* FileSystemService Tests
|
||||
*
|
||||
* Tests for the core filesystem service including backup behavior.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import * as path from 'node:path';
|
||||
import * as fs from 'node:fs/promises';
|
||||
import * as os from 'node:os';
|
||||
import { FileSystemService } from './filesystem-service.js';
|
||||
|
||||
// Create mock logger
|
||||
const createMockLogger = () => ({
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
createChild: vi.fn().mockReturnThis(),
|
||||
});
|
||||
|
||||
describe('FileSystemService', () => {
|
||||
let mockLogger: ReturnType<typeof createMockLogger>;
|
||||
let tempDir: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
mockLogger = createMockLogger();
|
||||
|
||||
// Create temp directory for testing
|
||||
const rawTempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dexto-fs-test-'));
|
||||
tempDir = await fs.realpath(rawTempDir);
|
||||
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Cleanup temp directory
|
||||
try {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
} catch {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
});
|
||||
|
||||
describe('Backup Behavior', () => {
|
||||
describe('writeFile', () => {
|
||||
it('should NOT create backup when enableBackups is false (default)', async () => {
|
||||
const fileSystemService = new FileSystemService(
|
||||
{
|
||||
allowedPaths: [tempDir],
|
||||
blockedPaths: [],
|
||||
blockedExtensions: [],
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
workingDirectory: tempDir,
|
||||
enableBackups: false,
|
||||
backupRetentionDays: 7,
|
||||
},
|
||||
mockLogger as any
|
||||
);
|
||||
await fileSystemService.initialize();
|
||||
|
||||
// Create initial file
|
||||
const testFile = path.join(tempDir, 'test.txt');
|
||||
await fs.writeFile(testFile, 'original content');
|
||||
|
||||
// Write new content (should NOT create backup)
|
||||
const result = await fileSystemService.writeFile(testFile, 'new content');
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.backupPath).toBeUndefined();
|
||||
|
||||
// Verify backup directory doesn't exist or is empty
|
||||
const backupDir = path.join(tempDir, '.dexto-backups');
|
||||
try {
|
||||
const files = await fs.readdir(backupDir);
|
||||
expect(files.length).toBe(0);
|
||||
} catch {
|
||||
// Backup dir doesn't exist, which is expected
|
||||
}
|
||||
});
|
||||
|
||||
it('should create backup when enableBackups is true', async () => {
|
||||
const fileSystemService = new FileSystemService(
|
||||
{
|
||||
allowedPaths: [tempDir],
|
||||
blockedPaths: [],
|
||||
blockedExtensions: [],
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
workingDirectory: tempDir,
|
||||
enableBackups: true,
|
||||
backupRetentionDays: 7,
|
||||
},
|
||||
mockLogger as any
|
||||
);
|
||||
await fileSystemService.initialize();
|
||||
|
||||
// Create initial file
|
||||
const testFile = path.join(tempDir, 'test.txt');
|
||||
await fs.writeFile(testFile, 'original content');
|
||||
|
||||
// Write new content (should create backup)
|
||||
const result = await fileSystemService.writeFile(testFile, 'new content');
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.backupPath).toBeDefined();
|
||||
expect(result.backupPath).toContain('.dexto');
|
||||
expect(result.backupPath).toContain('backup');
|
||||
|
||||
// Verify backup file exists and contains original content
|
||||
const backupContent = await fs.readFile(result.backupPath!, 'utf-8');
|
||||
expect(backupContent).toBe('original content');
|
||||
|
||||
// Verify new content was written
|
||||
const newContent = await fs.readFile(testFile, 'utf-8');
|
||||
expect(newContent).toBe('new content');
|
||||
});
|
||||
|
||||
it('should NOT create backup for new files even when enableBackups is true', async () => {
|
||||
const fileSystemService = new FileSystemService(
|
||||
{
|
||||
allowedPaths: [tempDir],
|
||||
blockedPaths: [],
|
||||
blockedExtensions: [],
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
workingDirectory: tempDir,
|
||||
enableBackups: true,
|
||||
backupRetentionDays: 7,
|
||||
},
|
||||
mockLogger as any
|
||||
);
|
||||
await fileSystemService.initialize();
|
||||
|
||||
// Write to a new file (no backup needed since file doesn't exist)
|
||||
const testFile = path.join(tempDir, 'new-file.txt');
|
||||
const result = await fileSystemService.writeFile(testFile, 'content', {
|
||||
createDirs: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.backupPath).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should respect per-call backup option over config', async () => {
|
||||
const fileSystemService = new FileSystemService(
|
||||
{
|
||||
allowedPaths: [tempDir],
|
||||
blockedPaths: [],
|
||||
blockedExtensions: [],
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
workingDirectory: tempDir,
|
||||
enableBackups: false, // Config says no backups
|
||||
backupRetentionDays: 7,
|
||||
},
|
||||
mockLogger as any
|
||||
);
|
||||
await fileSystemService.initialize();
|
||||
|
||||
// Create initial file
|
||||
const testFile = path.join(tempDir, 'test.txt');
|
||||
await fs.writeFile(testFile, 'original content');
|
||||
|
||||
// Write with explicit backup: true (should override config)
|
||||
const result = await fileSystemService.writeFile(testFile, 'new content', {
|
||||
backup: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.backupPath).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('editFile', () => {
|
||||
it('should NOT create backup when enableBackups is false (default)', async () => {
|
||||
const fileSystemService = new FileSystemService(
|
||||
{
|
||||
allowedPaths: [tempDir],
|
||||
blockedPaths: [],
|
||||
blockedExtensions: [],
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
workingDirectory: tempDir,
|
||||
enableBackups: false,
|
||||
backupRetentionDays: 7,
|
||||
},
|
||||
mockLogger as any
|
||||
);
|
||||
await fileSystemService.initialize();
|
||||
|
||||
// Create initial file
|
||||
const testFile = path.join(tempDir, 'test.txt');
|
||||
await fs.writeFile(testFile, 'hello world');
|
||||
|
||||
// Edit file (should NOT create backup)
|
||||
const result = await fileSystemService.editFile(testFile, {
|
||||
oldString: 'world',
|
||||
newString: 'universe',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.backupPath).toBeUndefined();
|
||||
|
||||
// Verify content was changed
|
||||
const content = await fs.readFile(testFile, 'utf-8');
|
||||
expect(content).toBe('hello universe');
|
||||
});
|
||||
|
||||
it('should create backup when enableBackups is true', async () => {
|
||||
const fileSystemService = new FileSystemService(
|
||||
{
|
||||
allowedPaths: [tempDir],
|
||||
blockedPaths: [],
|
||||
blockedExtensions: [],
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
workingDirectory: tempDir,
|
||||
enableBackups: true,
|
||||
backupRetentionDays: 7,
|
||||
},
|
||||
mockLogger as any
|
||||
);
|
||||
await fileSystemService.initialize();
|
||||
|
||||
// Create initial file
|
||||
const testFile = path.join(tempDir, 'test.txt');
|
||||
await fs.writeFile(testFile, 'hello world');
|
||||
|
||||
// Edit file (should create backup)
|
||||
const result = await fileSystemService.editFile(testFile, {
|
||||
oldString: 'world',
|
||||
newString: 'universe',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.backupPath).toBeDefined();
|
||||
|
||||
// Verify backup contains original content
|
||||
const backupContent = await fs.readFile(result.backupPath!, 'utf-8');
|
||||
expect(backupContent).toBe('hello world');
|
||||
|
||||
// Verify new content
|
||||
const content = await fs.readFile(testFile, 'utf-8');
|
||||
expect(content).toBe('hello universe');
|
||||
});
|
||||
|
||||
it('should respect per-call backup option over config', async () => {
|
||||
const fileSystemService = new FileSystemService(
|
||||
{
|
||||
allowedPaths: [tempDir],
|
||||
blockedPaths: [],
|
||||
blockedExtensions: [],
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
workingDirectory: tempDir,
|
||||
enableBackups: false, // Config says no backups
|
||||
backupRetentionDays: 7,
|
||||
},
|
||||
mockLogger as any
|
||||
);
|
||||
await fileSystemService.initialize();
|
||||
|
||||
// Create initial file
|
||||
const testFile = path.join(tempDir, 'test.txt');
|
||||
await fs.writeFile(testFile, 'hello world');
|
||||
|
||||
// Edit with explicit backup: true (should override config)
|
||||
const result = await fileSystemService.editFile(
|
||||
testFile,
|
||||
{
|
||||
oldString: 'world',
|
||||
newString: 'universe',
|
||||
},
|
||||
{ backup: true }
|
||||
);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.backupPath).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user