Back to catalog

utils-builder

Create utility functions and helper modules with pure functions and TypeScript types. Generates formatters (time, bytes, numbers), constants (colors, event types), and chart renderers (Canvas API). Keywords - utility function, helper, format, constants, utils, formatter, pure function, chart renderer

  • Category: discovered
  • Tags: github, community
  • Version: 1.0.0
  • Author: walid
  • Downloads: 0
  • Updated: 2025-11-22

Download the latest version (v1.0.0) from this page.

SKILL.md

---
name: utils-builder
description: Create utility functions and helper modules with pure functions and TypeScript types. Generates formatters (time, bytes, numbers), constants (colors, event types), and chart renderers (Canvas API). Keywords - utility function, helper, format, constants, utils, formatter, pure function, chart renderer
---

# Utils Builder - Poneglyph System

Creates utility functions and helper modules following the pure function patterns from the reference project, ensuring type safety, maintainability, and reusability across the Poneglyph System.

## When to Use This Skill

This skill should be used when users want to create utility functions, formatters, constants, or helper modules that follow Poneglyph's established patterns.

### Explicit Activation Contexts

Activate when:
- User explicitly requests utility creation ("create a utility for...", "build a formatter that...")
- User wants to create formatters (time, bytes, numbers, duration)
- User wants to define constants (colors, event types, severity levels, time ranges)
- User wants to create chart renderers or Canvas API utilities
- User asks about utility function patterns

### Proactive Detection Scenarios

Auto-suggest when detecting:
- Repeated formatting logic across multiple components (3+ instances)
- Hardcoded constants scattered throughout the codebase
- Side effects in functions that should be pure
- Missing type annotations in utility functions
- Conversational patterns: "create utility", "helper function", "format", "renderer"
- Duplicate chart rendering code

---

## Mission

Generate **production-ready utility modules** with **100% pure functions**, **explicit TypeScript 5.7+ types**, **comprehensive JSDoc documentation**, and **zero side effects**. Enforce Single Responsibility Principle, prevent god objects, and ensure utilities follow Poneglyph's **formatters.ts**, **constants.ts**, and **chartRenderer.ts** patterns from the reference project.

---

## Core Patterns

### Pattern 1: Pure Functions Only

All utility functions MUST be pure: same inputs always produce same outputs, with NO side effects.

❌ **WRONG - Impure function with side effects**:
```typescript
let globalCounter = 0;

function formatNumber(value: number): string {
  globalCounter++; // ❌ Modifies external state
  console.log('Formatting:', value); // ❌ Side effect (logging)
  return value.toFixed(2);
}
```

✅ **CORRECT - Pure function**:
```typescript
/**
 * Format a number to 2 decimal places
 * @param value - The number to format
 * @returns Formatted string with 2 decimal places
 * @example formatNumber(1234.567) // "1234.57"
 */
export function formatNumber(value: number): string {
  return value.toFixed(2);
}
```

**Auto-Check**:
- [ ] Function has no side effects (no console.log, no DOM manipulation, no external state changes)?
- [ ] Same inputs always produce same outputs?
- [ ] No mutations of input parameters?
- [ ] No async operations or timers?

---

### Pattern 2: Explicit TypeScript Types

All parameters and return values MUST have explicit type annotations. Never use implicit `any`.

❌ **WRONG - Missing type annotations**:
```typescript
// ❌ No parameter types, no return type
function formatRelativeTime(timestamp) {
  const now = Date.now();
  const diff = now - timestamp;
  return diff < 60000 ? 'just now' : 'a while ago';
}
```

✅ **CORRECT - Explicit types**:
```typescript
/**
 * Format a timestamp as relative time (e.g., "2 hours ago")
 * @param timestamp - Unix timestamp in milliseconds
 * @returns Human-readable relative time string
 * @example formatRelativeTime(Date.now() - 60000) // "1 minute ago"
 */
export function formatRelativeTime(timestamp: number): string {
  const now: number = Date.now();
  const diff: number = now - timestamp;

  if (diff < 60000) return 'just now';
  if (diff < 3600000) return `${Math.floor(diff / 60000)} minutes ago`;
  if (diff < 86400000) return `${Math.floor(diff / 3600000)} hours ago`;
  return `${Math.floor(diff / 86400000)} days ago`;
}
```

**Auto-Check**:
- [ ] All function parameters have type annotations?
- [ ] Return type is explicitly declared?
- [ ] No use of `any` type (unless absolutely necessary with justification)?
- [ ] Complex types use TypeScript interfaces or types?
- [ ] TSConfig strict mode enabled and passing?

---

### Pattern 3: Single Responsibility Principle

Each utility function does ONE thing and does it well. No god functions.

❌ **WRONG - God function doing multiple things**:
```typescript
// ❌ Does formatting, validation, AND transformation
function processData(value: number | string, options?: any) {
  if (typeof value === 'string') {
    value = parseFloat(value);
  }

  if (isNaN(value) || value < 0) {
    throw new Error('Invalid value');
  }

  if (options?.format === 'bytes') {
    return formatBytes(value);
  } else if (options?.format === 'duration') {
    return formatDuration(value);
  } else {
    return value.toFixed(2);
  }
}
```

✅ **CORRECT - Separate focused functions**:
```typescript
/**
 * Format bytes to human-readable size (e.g., "1.5 KB")
 * @param bytes - Number of bytes
 * @returns Formatted string with appropriate unit
 * @example formatBytes(1536) // "1.5 KB"
 */
export function formatBytes(bytes: number): string {
  if (bytes === 0) return '0 B';
  const k = 1024;
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
}

/**
 * Format milliseconds to human-readable duration (e.g., "2h 30m")
 * @param ms - Duration in milliseconds
 * @returns Formatted duration string
 * @example formatDuration(9000000) // "2h 30m"
 */
export function formatDuration(ms: number): string {
  const seconds = Math.floor(ms / 1000);
  const minutes = Math.floor(seconds / 60);
  const hours = Math.floor(minutes / 60);

  if (hours > 0) return `${hours}h ${minutes % 60}m`;
  if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
  return `${seconds}s`;
}
```

**Auto-Check**:
- [ ] Each function has a single, clear purpose?
- [ ] Function name clearly describes what it does (verb-first)?
- [ ] Function is under 30 lines of code?
- [ ] No nested conditionals deeper than 2 levels?

---

### Pattern 4: Individual Named Exports

Export functions individually, NOT as default exports. Makes tree-shaking easier.

❌ **WRONG - Default export or namespace export**:
```typescript
// ❌ Default export
export default {
  formatNumber,
  formatBytes,
  formatDuration
};

// ❌ Namespace export
export const Formatters = {
  formatNumber,
  formatBytes,
  formatDuration
};
```

✅ **CORRECT - Individual named exports**:
```typescript
/**
 * Format a number with thousand separators
 */
export function formatNumber(value: number): string {
  return value.toLocaleString('en-US');
}

/**
 * Format bytes to human-readable size
 */
export function formatBytes(bytes: number): string {
  // Implementation
  return `${bytes} B`;
}

/**
 * Format milliseconds to duration
 */
export function formatDuration(ms: number): string {
  // Implementation
  return `${ms}ms`;
}

// Usage:
// import { formatNumber, formatBytes } from './formatters';
```

**Auto-Check**:
- [ ] All functions are exported individually with `export function`?
- [ ] No default exports?
- [ ] No object/namespace wrappers around functions?
- [ ] Imports are specific (not `import * as Utils`)?

---

### Pattern 5: Comprehensive JSDoc Comments

Every utility function MUST have JSDoc with description, parameters, return value, and example.

❌ **WRONG - Missing or incomplete JSDoc**:
```typescript
// ❌ No JSDoc at all
export function formatRelativeTime(timestamp: number): string {
  // Implementation
  return 'just now';
}

// ❌ Incomplete JSDoc
/**
 * Format time
 */
export function formatRelativeTime(timestamp: number): string {
  return 'just now';
}
```

✅ **CORRECT - Complete JSDoc**:
```typescript
/**
 * Format a Unix timestamp as relative time (e.g., "2 hours ago", "just now")
 *
 * Converts a timestamp to a human-readable relative time string.
 * Uses progressive time units: seconds, minutes, hours, days.
 *
 * @param timestamp - Unix timestamp in milliseconds (from Date.now() or .getTime())
 * @returns Human-readable relative time string
 *
 * @example
 * formatRelativeTime(Date.now() - 1000) // "just now"
 * formatRelativeTime(Date.now() - 60000) // "1 minute ago"
 * formatRelativeTime(Date.now() - 3600000) // "1 hour ago"
 * formatRelativeTime(Date.now() - 86400000) // "1 day ago"
 */
export function formatRelativeTime(timestamp: number): string {
  const now: number = Date.now();
  const diff: number = now - timestamp;

  if (diff < 60000) return 'just now';
  if (diff < 3600000) return `${Math.floor(diff / 60000)} minute${Math.floor(diff / 60000) === 1 ? '' : 's'} ago`;
  if (diff < 86400000) return `${Math.floor(diff / 3600000)} hour${Math.floor(diff / 3600000) === 1 ? '' : 's'} ago`;
  return `${Math.floor(diff / 86400000)} day${Math.floor(diff / 86400000) === 1 ? '' : 's'} ago`;
}
```

**Auto-Check**:
- [ ] Every exported function has JSDoc comment?
- [ ] JSDoc includes description (multi-line for complex functions)?
- [ ] All `@param` tags present with types and descriptions?
- [ ] `@returns` tag present with description?
- [ ] At least one `@example` showing usage?

---

## Anti-Patterns

### Anti-Pattern 1: Mixing Side Effects with Pure Logic

❌ **ANTI-PATTERN - Side effects in utility**:
```typescript
// ❌ Logging, DOM manipulation, API calls in utility
export function formatAndLog(value: number): string {
  console.log('Formatting value:', value); // ❌ Side effect
  document.title = `Value: ${value}`; // ❌ DOM manipulation

  fetch('/api/log', {
    method: 'POST',
    body: JSON.stringify({ value })
  }); // ❌ Network call

  return value.toFixed(2);
}
```

✅ **CORRECT - Pure function, caller handles side effects**:
```typescript
/**
 * Format a number to 2 decimal places
 * @param value - Number to format
 * @returns Formatted string
 */
export function formatNumber(value: number): string {
  return value.toFixed(2);
}

// Caller handles side effects separately:
// const formatted = formatNumber(123.456);
// console.log('Formatted:', formatted); // Side effect in component
```

**Why It Matters**: Pure functions are predictable, testable, and cacheable. Side effects make testing difficult and introduce unpredictable behavior.

---

### Anti-Pattern 2: Using Mutable Constants

❌ **ANTI-PATTERN - Mutable constants**:
```typescript
// ❌ Using 'let' or mutable objects
export let COLOR_PRIMARY = '#3b82f6'; // ❌ Can be reassigned
export const SEVERITY_LEVELS = ['debug', 'info']; // ❌ Array is mutable

// ❌ Mutable nested objects
export const CHART_CONFIG = {
  colors: {
    primary: '#3b82f6'
  }
};
// Caller can do: CHART_CONFIG.colors.primary = 'red'; ❌
```

✅ **CORRECT - Immutable constants with 'as const'**:
```typescript
/**
 * Primary color palette for Poneglyph System
 * Uses blue-based theme matching Tailwind primary-500
 */
export const COLOR_PALETTE = {
  primary: '#3b82f6',
  secondary: '#6366f1',
  success: '#10b981',
  warning: '#f59e0b',
  error: '#ef4444',
  info: '#0ea5e9'
} as const;

/**
 * Event severity levels in ascending order
 * Maps to PostgreSQL ENUM severity_level
 */
export const SEVERITY_LEVELS = [
  'debug',
  'info',
  'warning',
  'error',
  'critical'
] as const;

/**
 * Type-safe severity level union
 */
export type SeverityLevel = typeof SEVERITY_LEVELS[number];
```

**Why It Matters**: `as const` provides deep readonly immutability and enables literal type inference, preventing accidental mutations and improving type safety.

---

### Anti-Pattern 3: Missing Type Guards for Union Types

❌ **ANTI-PATTERN - No type guards for union types**:
```typescript
// ❌ No type narrowing
export function formatValue(value: number | string): string {
  // ❌ TypeScript error: 'value' might be string
  return value.toFixed(2);
}
```

✅ **CORRECT - Type guards with explicit checks**:
```typescript
/**
 * Format a value to 2 decimal places
 * Handles both number and numeric string inputs
 * @param value - Number or numeric string
 * @returns Formatted string or 'N/A' for invalid inputs
 * @example
 * formatValue(123.456) // "123.46"
 * formatValue("123.456") // "123.46"
 * formatValue("invalid") // "N/A"
 */
export function formatValue(value: number | string): string {
  // Type guard
  const numValue = typeof value === 'string' ? parseFloat(value) : value;

  if (isNaN(numValue)) {
    return 'N/A';
  }

  return numValue.toFixed(2);
}
```

**Why It Matters**: Type guards enable TypeScript to narrow union types, ensuring type safety and preventing runtime errors from incorrect type assumptions.

---

### Anti-Pattern 4: God Objects with Multiple Responsibilities

❌ **ANTI-PATTERN - Single object doing everything**:
```typescript
// ❌ One giant Utils object
export const Utils = {
  // Time formatting
  formatTime: (ms: number) => { /* ... */ },
  parseTime: (str: string) => { /* ... */ },

  // Number formatting
  formatNumber: (n: number) => { /* ... */ },
  parseNumber: (str: string) => { /* ... */ },

  // String manipulation
  capitalize: (str: string) => { /* ... */ },
  truncate: (str: string, len: number) => { /* ... */ },

  // Array operations
  unique: <T>(arr: T[]) => { /* ... */ },
  groupBy: <T>(arr: T[], key: keyof T) => { /* ... */ },

  // Chart rendering
  renderChart: (ctx: CanvasRenderingContext2D) => { /* ... */ }
};
```

✅ **CORRECT - Separate modules by responsibility**:
```typescript
// formatters.ts - Pure formatting functions
export function formatRelativeTime(timestamp: number): string { /* ... */ }
export function formatBytes(bytes: number): string { /* ... */ }
export function formatDuration(ms: number): string { /* ... */ }
export function formatNumber(value: number): string { /* ... */ }

// constants.ts - Immutable configuration
export const COLOR_PALETTE = { /* ... */ } as const;
export const EVENT_TYPES = [ /* ... */ ] as const;
export const SEVERITY_LEVELS = [ /* ... */ ] as const;

// chartRenderer.ts - Canvas rendering class
export class ChartRenderer {
  constructor(canvas: HTMLCanvasElement, config: ChartConfig) { /* ... */ }
  drawBars(data: ChartDataPoint[]): void { /* ... */ }
  clear(): void { /* ... */ }
}

// arrayUtils.ts - Array operations (if needed)
export function unique<T>(arr: T[]): T[] { /* ... */ }
export function groupBy<T>(arr: T[], key: keyof T): Map<T[keyof T], T[]> { /* ... */ }
```

**Why It Matters**: Separation of concerns improves maintainability, enables tree-shaking, makes testing easier, and prevents merge conflicts in team environments.

---

### Anti-Pattern 5: Unclear Function Names

❌ **ANTI-PATTERN - Vague or abbreviated names**:
```typescript
// ❌ Unclear what these do
export function proc(val: any): any { /* ... */ }
export function fmt(v: number): string { /* ... */ }
export function calc(a: number, b: number): number { /* ... */ }
export function getData(): any[] { /* ... */ }
```

✅ **CORRECT - Clear, descriptive names**:
```typescript
/**
 * Format milliseconds to human-readable duration
 */
export function formatDuration(milliseconds: number): string { /* ... */ }

/**
 * Calculate average value from array of numbers
 */
export function calculateAverage(values: number[]): number { /* ... */ }

/**
 * Convert bytes to kilobytes
 */
export function bytesToKilobytes(bytes: number): number { /* ... */ }
```

**Why It Matters**: Function names are documentation. Clear names reduce cognitive load and make code self-documenting.

---

## Validation Checklist

Before marking skill complete, verify:

### Code Quality (35 points)
- [ ] All functions are pure (no side effects, same input = same output) (10 pts)
- [ ] TypeScript strict mode enabled and all types explicit (no implicit any) (10 pts)
- [ ] Each function follows Single Responsibility Principle (under 30 lines) (10 pts)
- [ ] All exports are individual named exports (no default exports) (5 pts)

### Documentation Quality (25 points)
- [ ] Every function has comprehensive JSDoc (description, params, returns, example) (15 pts)
- [ ] JSDoc examples are runnable and demonstrate actual usage (5 pts)
- [ ] Comments explain WHY, not WHAT (code is self-documenting) (5 pts)

### Type Safety (25 points)
- [ ] All parameters have explicit type annotations (5 pts)
- [ ] All return types are explicitly declared (5 pts)
- [ ] Union types use type guards for narrowing (5 pts)
- [ ] Constants use 'as const' for deep immutability (5 pts)
- [ ] Complex types use interfaces or type aliases (5 pts)

### Module Organization (15 points)
- [ ] Utilities grouped by responsibility (formatters, constants, renderers) (5 pts)
- [ ] No god objects or multi-purpose modules (5 pts)
- [ ] Clear file naming (formatters.ts, constants.ts, chartRenderer.ts) (5 pts)

### Total: __/100

**Minimum Score**: 90/100 for production use

---

## Standard Utility Modules

### formatters.ts Structure

```typescript
/**
 * Poneglyph System - Formatter Utilities
 * Pure functions for formatting time, numbers, bytes, and durations
 * Version: 1.0.0
 * Generated: YYYY-MM-DD
 */

/**
 * Format Unix timestamp as relative time
 * @param timestamp - Unix timestamp in milliseconds
 * @returns Human-readable relative time (e.g., "2 hours ago")
 * @example formatRelativeTime(Date.now() - 3600000) // "1 hour ago"
 */
export function formatRelativeTime(timestamp: number): string {
  // Implementation
}

/**
 * Format number with thousand separators
 * @param value - Number to format
 * @returns Formatted string (e.g., "1,234,567")
 * @example formatNumber(1234567) // "1,234,567"
 */
export function formatNumber(value: number): string {
  // Implementation
}

/**
 * Format bytes to human-readable size
 * @param bytes - Number of bytes
 * @returns Formatted string with unit (e.g., "1.5 MB")
 * @example formatBytes(1572864) // "1.5 MB"
 */
export function formatBytes(bytes: number): string {
  // Implementation
}

/**
 * Format milliseconds to duration string
 * @param ms - Duration in milliseconds
 * @returns Formatted duration (e.g., "2h 30m")
 * @example formatDuration(9000000) // "2h 30m"
 */
export function formatDuration(ms: number): string {
  // Implementation
}
```

### constants.ts Structure

```typescript
/**
 * Poneglyph System - Application Constants
 * Immutable configuration values for colors, event types, and time ranges
 * Version: 1.0.0
 * Generated: YYYY-MM-DD
 */

/**
 * Color palette for Poneglyph System
 * Matches Chart.js theme and Tailwind CSS colors
 */
export const COLOR_PALETTE = {
  primary: '#3b82f6',
  secondary: '#6366f1',
  success: '#10b981',
  warning: '#f59e0b',
  error: '#ef4444',
  info: '#0ea5e9',
  gray: {
    50: '#f9fafb',
    100: '#f3f4f6',
    200: '#e5e7eb',
    500: '#6b7280',
    700: '#374151',
    900: '#111827'
  }
} as const;

/**
 * Event type categories
 * Maps to backend EventTypeSchema
 */
export const EVENT_TYPES = [
  'tool_use_started',
  'tool_use_completed',
  'user_prompt_submitted',
  'agent_started',
  'agent_completed',
  'agent_stopped',
  'context_compacted',
  'subagent_stopped'
] as const;

export type EventType = typeof EVENT_TYPES[number];

/**
 * Severity levels in ascending order
 * Maps to PostgreSQL ENUM severity_level
 */
export const SEVERITY_LEVELS = [
  'debug',
  'info',
  'warning',
  'error',
  'critical'
] as const;

export type SeverityLevel = typeof SEVERITY_LEVELS[number];

/**
 * Time range options for charts and filters
 */
export const TIME_RANGES = {
  '1m': 60 * 1000,
  '3m': 3 * 60 * 1000,
  '5m': 5 * 60 * 1000,
  '10m': 10 * 60 * 1000,
  '1h': 60 * 60 * 1000,
  '24h': 24 * 60 * 60 * 1000,
  '7d': 7 * 24 * 60 * 60 * 1000,
  '30d': 30 * 24 * 60 * 60 * 1000
} as const;

export type TimeRangeKey = keyof typeof TIME_RANGES;
```

### chartRenderer.ts Structure

```typescript
/**
 * Poneglyph System - Canvas Chart Renderer
 * High-performance c