feat: add runtime context awareness — system.info tool + date/time in system prompt

- assembleSystemPrompt() now injects '# Runtime Context' with current date/time
- New system.info tool: date, time, hostname, platform, arch, uptime, memory, Node.js version
- Tool available in all profiles (minimal/messaging/coding/full)
- 983 tests passing (+7 new)
This commit is contained in:
William Valentin
2026-02-07 16:22:17 -08:00
parent 33f6f142bc
commit 8bf88049bf
9 changed files with 228 additions and 16 deletions
+3
View File
@@ -4,6 +4,7 @@ export { fileWriteTool } from './file-write.js';
export { fileEditTool } from './file-edit.js';
export { filePatchTool } from './file-patch.js';
export { fileListTool } from './file-list.js';
export { systemInfoTool } from './system-info.js';
export { webFetchTool } from './web-fetch.js';
export { createMediaSendTool } from './media-send.js';
export { createImageAnalyzeTool } from './image-analyze.js';
@@ -31,6 +32,7 @@ import { fileWriteTool } from './file-write.js';
import { fileEditTool } from './file-edit.js';
import { filePatchTool } from './file-patch.js';
import { fileListTool } from './file-list.js';
import { systemInfoTool } from './system-info.js';
import { webFetchTool } from './web-fetch.js';
import { createMediaSendTool } from './media-send.js';
import { createImageAnalyzeTool } from './image-analyze.js';
@@ -47,6 +49,7 @@ export const allBuiltinTools: Tool[] = [
fileEditTool,
filePatchTool,
fileListTool,
systemInfoTool,
webFetchTool,
];
+84
View File
@@ -0,0 +1,84 @@
import { describe, it, expect } from 'vitest';
import { systemInfoTool } from './system-info.js';
describe('system.info tool', () => {
// ── Metadata ─────────────────────────────────────────────────────────────
it('has correct metadata', () => {
expect(systemInfoTool.name).toBe('system.info');
expect(systemInfoTool.description).toBeTruthy();
expect(systemInfoTool.inputSchema.type).toBe('object');
});
// ── Execution ────────────────────────────────────────────────────────────
it('returns success with system info', async () => {
const result = await systemInfoTool.execute({});
expect(result.success).toBe(true);
expect(typeof result.output).toBe('string');
expect(result.output!.length).toBeGreaterThan(0);
});
it('output contains expected fields', async () => {
const result = await systemInfoTool.execute({});
expect(result.success).toBe(true);
const output = result.output!;
const expectedFields = [
'Date:',
'Time:',
'Hostname:',
'Platform:',
'Architecture:',
'Node.js:',
'Uptime:',
'Memory Total:',
];
for (const field of expectedFields) {
expect(output).toContain(field);
}
});
it('output contains valid ISO timestamp', async () => {
const result = await systemInfoTool.execute({});
expect(result.success).toBe(true);
const output = result.output!;
// Find the line containing 'ISO 8601:'
const isoLine = output.split('\n').find((line) => line.includes('ISO 8601:'));
expect(isoLine).toBeTruthy();
// Extract the ISO string and validate it
const isoMatch = isoLine!.match(/ISO 8601:\s*(.+)/);
expect(isoMatch).toBeTruthy();
const isoString = isoMatch![1].trim();
const parsed = new Date(isoString);
expect(parsed.toISOString()).toBe(isoString);
});
it('output contains working directory', async () => {
const result = await systemInfoTool.execute({});
expect(result.success).toBe(true);
expect(result.output).toContain('Working Dir:');
});
// ── Idempotency ──────────────────────────────────────────────────────────
it('handles repeated calls', async () => {
const result1 = await systemInfoTool.execute({});
const result2 = await systemInfoTool.execute({});
expect(result1.success).toBe(true);
expect(result2.success).toBe(true);
expect(typeof result1.output).toBe('string');
expect(typeof result2.output).toBe('string');
expect(result1.output!.length).toBeGreaterThan(0);
expect(result2.output!.length).toBeGreaterThan(0);
});
});
+63
View File
@@ -0,0 +1,63 @@
import os from 'os';
import type { Tool, ToolResult } from '../types.js';
function formatUptime(seconds: number): string {
const days = Math.floor(seconds / 86400);
const hours = Math.floor((seconds % 86400) / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return `${days}d ${hours}h ${minutes}m`;
}
function formatBytes(bytes: number): string {
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
}
export const systemInfoTool: Tool = {
name: 'system.info',
description: 'Get current system information including date, time, hostname, OS, platform, architecture, uptime, Node.js version, and memory usage.',
inputSchema: {
type: 'object',
properties: {},
required: [],
},
execute: async (_rawArgs: unknown): Promise<ToolResult> => {
try {
const now = new Date();
const dateStr = now.toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
});
const timeStr = now.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
timeZoneName: 'short',
hour12: false,
});
const isoStr = now.toISOString();
const totalMem = os.totalmem();
const freeMem = os.freemem();
const lines = [
`Date: ${dateStr}`,
`Time: ${timeStr}`,
`ISO 8601: ${isoStr}`,
`Hostname: ${os.hostname()}`,
`Platform: ${os.platform()}`,
`Architecture: ${os.arch()}`,
`OS Release: ${os.release()}`,
`Uptime: ${formatUptime(os.uptime())}`,
`Node.js: ${process.version}`,
`Memory Total: ${formatBytes(totalMem)}`,
`Memory Free: ${formatBytes(freeMem)}`,
`Working Dir: ${process.cwd()}`,
];
return { success: true, output: lines.join('\n') };
} catch (error) {
return { success: false, output: '', error: error instanceof Error ? error.message : String(error) };
}
},
};
+1
View File
@@ -14,4 +14,5 @@ export { fileReadTool } from './builtin/file-read.js';
export { fileWriteTool } from './builtin/file-write.js';
export { fileEditTool } from './builtin/file-edit.js';
export { fileListTool } from './builtin/file-list.js';
export { systemInfoTool } from './builtin/system-info.js';
export { webFetchTool } from './builtin/web-fetch.js';
+3
View File
@@ -9,11 +9,13 @@ const PROFILE_TOOLS: Record<ToolProfile, Set<string>> = {
'file.read',
'file.list',
'web.fetch',
'system.info',
]),
messaging: new Set([
'file.read',
'file.list',
'web.fetch',
'system.info',
'memory.read',
'memory.write',
'memory.search',
@@ -23,6 +25,7 @@ const PROFILE_TOOLS: Record<ToolProfile, Set<string>> = {
'file.read',
'file.list',
'web.fetch',
'system.info',
'memory.read',
'memory.write',
'memory.search',