spb/khaelor Public
KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.
TypeScript 82.9%
HTML 14.9%
CSS 1.1%
JavaScript 0.7%
1/**2 * KHAELOR3 * File: src/tools/format.ts4 * Description: Shared formatting helpers for tool outputs — line numbering, paths, durations, header checks.5 *6 * Author: Simon-Pierre Boucher7 * Contact: contact@spboucher.ai8 */910import * as path from "node:path";1112/** Right-aligned line number + `→` separator (TOOL_PROTOCOL §2.3). */13export function numberedLine(lineNumber: number, text: string): string {14 return `${String(lineNumber).padStart(6)}→${text}`;15}1617/** Resolve a possibly-relative tool path against the workspace cwd. */18export function resolveToolPath(cwd: string, p: string): string {19 return path.resolve(cwd, p);20}2122/** Workspace-relative display path (falls back to absolute outside the project). */23export function displayPath(cwd: string, abs: string): string {24 const rel = path.relative(cwd, abs);25 if (rel.length === 0) return ".";26 return rel.startsWith("..") ? abs : rel.split(path.sep).join("/");27}2829/** `3.2s`-style duration. */30export function formatDuration(ms: number): string {31 return `${(ms / 1000).toFixed(1)}s`;32}3334/** `04:32`-style elapsed time (hh:mm:ss above one hour). */35export function formatElapsed(ms: number): string {36 const totalSeconds = Math.floor(ms / 1000);37 const seconds = totalSeconds % 60;38 const minutes = Math.floor(totalSeconds / 60) % 60;39 const hours = Math.floor(totalSeconds / 3600);40 const mm = String(minutes).padStart(2, "0");41 const ss = String(seconds).padStart(2, "0");42 return hours > 0 ? `${String(hours).padStart(2, "0")}:${mm}:${ss}` : `${mm}:${ss}`;43}4445/** Count logical lines: a trailing newline does not open a new line. */46export function countContentLines(content: string): number {47 if (content.length === 0) return 0;48 const lines = content.split("\n");49 if (lines[lines.length - 1] === "") lines.pop();50 return lines.length;51}5253/** Extensions that require the KHAELOR author header (CLAUDE.md §2, Rule #0). */54const HEADER_REQUIRED_EXTENSIONS = new Set([55 ".ts",56 ".tsx",57 ".js",58 ".mjs",59 ".cjs",60 ".sh",61 ".rs",62 ".md",63]);6465/** Paths exempt from the header requirement (vendored/generated). */66const HEADER_EXEMPT_SEGMENTS = ["node_modules", "references", "dist", "build"];6768/** Does this new file need the KHAELOR header (TOOL_PROTOCOL §3.2)? */69export function requiresKhaelorHeader(cwd: string, abs: string): boolean {70 if (!HEADER_REQUIRED_EXTENSIONS.has(path.extname(abs))) return false;71 const rel = path.relative(cwd, abs);72 if (rel.startsWith("..") || path.isAbsolute(rel)) return false; // outside the project73 const segments = rel.split(path.sep);74 return !segments.some((segment) => HEADER_EXEMPT_SEGMENTS.includes(segment));75}7677/** Mirrors scripts/check-headers.sh: KHAELOR marker + author + contact within the first 12 lines. */78export function hasKhaelorHeader(content: string): boolean {79 const head = content.split("\n").slice(0, 12).join("\n");80 return (81 head.includes("KHAELOR") &&82 head.includes("Simon-Pierre Boucher") &&83 head.includes("contact@spboucher.ai")84 );85}8687/** The header reminder appended to headerless new source files (TOOL_PROTOCOL §3.2, verbatim). */88export const HEADER_REMINDER_NOTE = [89 "NOTE: This new source file is missing the mandatory KHAELOR author header",90 "(Author: Simon-Pierre Boucher · Contact: contact@spboucher.ai · File · Description).",91 "Add it now — the header lint check fails the build without it.",92].join("\n");93