Commit eddb00cc99ad
2026-02-16 14:18:45
1 parent
805125c
Changed files (2)
src
pi
tools
src/pi/tools/status.test.ts
@@ -0,0 +1,70 @@
+/**
+ * Tests for Status Tool
+ *
+ * RED phase: Write these tests first, verify they fail
+ * GREEN phase: Implement minimal code to pass
+ * REFACTOR phase: Improve structure while keeping tests green
+ */
+
+import { describe, it, expect } from "vitest";
+import { statusTool } from "./status.js";
+
+describe("Status Tool", () => {
+ it("should have correct tool metadata", () => {
+ expect(statusTool.name).toBe("status");
+ expect(statusTool.label).toBeDefined();
+ expect(statusTool.description).toBeDefined();
+ expect(statusTool.description).toContain("system");
+ });
+
+ it("should have no required parameters", () => {
+ expect(statusTool.parameters).toBeDefined();
+ // Empty object schema = no parameters
+ });
+
+ it("should execute and return system status", async () => {
+ const result = await statusTool.execute(
+ "test-call-id",
+ {},
+ new AbortController().signal,
+ () => {}
+ );
+
+ expect(result).toBeDefined();
+ expect(result.content).toBeDefined();
+ expect(Array.isArray(result.content)).toBe(true);
+ expect(result.content.length).toBeGreaterThan(0);
+ });
+
+ it("should return text content with system info", async () => {
+ const result = await statusTool.execute(
+ "test-call-id",
+ {},
+ new AbortController().signal,
+ () => {}
+ );
+
+ const textContent = result.content.find((c: any) => c.type === "text");
+ expect(textContent).toBeDefined();
+
+ const text = (textContent as any).text.toLowerCase();
+ expect(text).toContain("uptime");
+ expect(text).toContain("memory");
+ expect(text).toContain("load");
+ });
+
+ it("should include numeric values in status", async () => {
+ const result = await statusTool.execute(
+ "test-call-id",
+ {},
+ new AbortController().signal,
+ () => {}
+ );
+
+ const textContent = result.content.find((c: any) => c.type === "text");
+ const text = (textContent as any).text;
+
+ // Should contain numbers (uptime seconds, memory MB, load average)
+ expect(text).toMatch(/\d+/);
+ });
+});
src/pi/tools/status.ts
@@ -0,0 +1,68 @@
+/**
+ * Status Tool - Show system status (uptime, memory, load)
+ */
+
+import { Type } from "@sinclair/typebox";
+import type { AgentTool } from "@mariozechner/pi-agent-core";
+import * as os from "os";
+
+export const statusTool: AgentTool = {
+ name: "status",
+ label: "System Status",
+ description: "Show system status including uptime, memory usage, and load average",
+ parameters: Type.Object({}),
+ execute: async (toolCallId, params, signal, onUpdate) => {
+ // Get system information
+ const uptimeSeconds = os.uptime();
+ const totalMemory = os.totalmem();
+ const freeMemory = os.freemem();
+ const usedMemory = totalMemory - freeMemory;
+ const memoryUsagePercent = ((usedMemory / totalMemory) * 100).toFixed(1);
+ const loadAvg = os.loadavg();
+
+ // Format uptime
+ const hours = Math.floor(uptimeSeconds / 3600);
+ const minutes = Math.floor((uptimeSeconds % 3600) / 60);
+ const uptimeFormatted = `${hours}h ${minutes}m`;
+
+ // Format memory
+ const totalMemoryGB = (totalMemory / (1024 * 1024 * 1024)).toFixed(2);
+ const usedMemoryGB = (usedMemory / (1024 * 1024 * 1024)).toFixed(2);
+
+ const statusText = `System Status:
+
+Uptime: ${uptimeFormatted} (${Math.floor(uptimeSeconds)}s)
+
+Memory:
+ Total: ${totalMemoryGB} GB
+ Used: ${usedMemoryGB} GB (${memoryUsagePercent}%)
+ Free: ${(freeMemory / (1024 * 1024 * 1024)).toFixed(2)} GB
+
+Load Average:
+ 1 min: ${loadAvg[0].toFixed(2)}
+ 5 min: ${loadAvg[1].toFixed(2)}
+ 15 min: ${loadAvg[2].toFixed(2)}
+
+Platform: ${os.platform()} ${os.arch()}
+CPUs: ${os.cpus().length}`;
+
+ return {
+ content: [
+ {
+ type: "text" as const,
+ text: statusText,
+ },
+ ],
+ details: {
+ uptime: uptimeSeconds,
+ memory: {
+ total: totalMemory,
+ used: usedMemory,
+ free: freeMemory,
+ usagePercent: parseFloat(memoryUsagePercent),
+ },
+ load: loadAvg,
+ },
+ };
+ },
+};