Files
codetyper.cli/src/services/debugging-service.ts
Carlos Gutierrez 0062e5d9d9 Terminal-based AI coding agent with interactive TUI for autonomous code generation.
Features:
  - Interactive TUI with React/Ink
  - Autonomous agent with tool calls (bash, read, write, edit, glob, grep)
  - Permission system with pattern-based rules
  - Session management with auto-compaction
  - Dual providers: GitHub Copilot and Ollama
  - MCP server integration
  - Todo panel and theme system
  - Streaming responses
  - GitHub-compatible project context
2026-01-27 23:33:06 -05:00

209 lines
5.0 KiB
TypeScript

/**
* Debugging Detection Service
*
* Detects debugging-related requests and provides debugging context.
*/
import {
DEBUGGING_SYSTEM_PROMPT,
DEBUGGING_CONTEXT_TEMPLATE,
} from "@prompts/system/debugging";
export interface DebugContext {
isDebugging: boolean;
errorMessage?: string;
location?: string;
expected?: string;
actual?: string;
stackTrace?: string;
debugType: DebugType;
}
export type DebugType =
| "error"
| "bug"
| "fix"
| "issue"
| "crash"
| "broken"
| "notworking"
| "none";
const DEBUG_KEYWORDS: Record<DebugType, string[]> = {
error: [
"error",
"exception",
"throw",
"thrown",
"stack trace",
"stacktrace",
"traceback",
],
bug: ["bug", "buggy", "glitch", "defect"],
fix: ["fix", "fixing", "repair", "resolve", "solved"],
issue: ["issue", "problem", "trouble", "wrong"],
crash: ["crash", "crashed", "crashing", "dies", "killed"],
broken: ["broken", "break", "breaks", "broke"],
notworking: [
"not working",
"doesn't work",
"doesn't work",
"won't work",
"isn't working",
"stopped working",
"no longer works",
"fails",
"failing",
"failed",
],
none: [],
};
const ERROR_PATTERNS = [
// JavaScript/TypeScript errors
/(?:TypeError|ReferenceError|SyntaxError|RangeError|Error):\s*.+/i,
// Stack trace patterns
/at\s+\w+\s+\([^)]+:\d+:\d+\)/,
/^\s*at\s+.+\s+\(.+\)$/m,
// Common error messages
/cannot read propert(?:y|ies) of (?:undefined|null)/i,
/is not a function/i,
/is not defined/i,
/unexpected token/i,
/failed to/i,
/unable to/i,
// Exit codes
/exit(?:ed)? (?:with )?(?:code|status) \d+/i,
// HTTP errors
/(?:4|5)\d{2}\s+(?:error|bad|not found|internal|forbidden)/i,
];
const extractErrorMessage = (input: string): string | undefined => {
for (const pattern of ERROR_PATTERNS) {
const match = input.match(pattern);
if (match) {
return match[0];
}
}
return undefined;
};
const extractStackTrace = (input: string): string | undefined => {
const lines = input.split("\n");
const stackLines: string[] = [];
let inStack = false;
for (const line of lines) {
if (/^\s*at\s+/.test(line) || /Error:/.test(line)) {
inStack = true;
stackLines.push(line);
} else if (inStack && line.trim() === "") {
break;
} else if (inStack) {
stackLines.push(line);
}
}
return stackLines.length > 0 ? stackLines.join("\n") : undefined;
};
const extractLocation = (input: string): string | undefined => {
// Match file:line:column patterns
const patterns = [
/([a-zA-Z0-9_\-./]+\.[a-zA-Z]+):(\d+)(?::(\d+))?/,
/(?:in|at|file)\s+['"]?([^'":\s]+)['"]?\s*(?:line\s*)?(\d+)?/i,
];
for (const pattern of patterns) {
const match = input.match(pattern);
if (match) {
const file = match[1];
const line = match[2];
const col = match[3];
return col ? `${file}:${line}:${col}` : line ? `${file}:${line}` : file;
}
}
return undefined;
};
const detectDebugType = (input: string): DebugType => {
const lowerInput = input.toLowerCase();
for (const [type, keywords] of Object.entries(DEBUG_KEYWORDS)) {
if (type === "none") continue;
for (const keyword of keywords) {
if (lowerInput.includes(keyword)) {
return type as DebugType;
}
}
}
// Check for error patterns even without keywords
if (ERROR_PATTERNS.some((pattern) => pattern.test(input))) {
return "error";
}
return "none";
};
export const detectDebuggingRequest = (input: string): DebugContext => {
const debugType = detectDebugType(input);
if (debugType === "none") {
return {
isDebugging: false,
debugType: "none",
};
}
return {
isDebugging: true,
debugType,
errorMessage: extractErrorMessage(input),
location: extractLocation(input),
stackTrace: extractStackTrace(input),
};
};
export const buildDebuggingContext = (context: DebugContext): string => {
if (!context.isDebugging) {
return "";
}
let debugContext = DEBUGGING_CONTEXT_TEMPLATE.replace(
"{{errorMessage}}",
context.errorMessage || "Not specified",
)
.replace("{{location}}", context.location || "Not specified")
.replace("{{expected}}", context.expected || "Not specified")
.replace("{{actual}}", context.actual || "Not specified");
if (context.stackTrace) {
debugContext += `\n**Stack Trace**:\n\`\`\`\n${context.stackTrace}\n\`\`\`\n`;
}
return debugContext;
};
export const getDebuggingPrompt = (): string => {
return DEBUGGING_SYSTEM_PROMPT;
};
export const enhancePromptForDebugging = (
basePrompt: string,
userInput: string,
): { prompt: string; context: DebugContext } => {
const context = detectDebuggingRequest(userInput);
if (!context.isDebugging) {
return { prompt: basePrompt, context };
}
const debugPrompt = getDebuggingPrompt();
const debugContext = buildDebuggingContext(context);
const enhancedPrompt = `${basePrompt}\n\n${debugPrompt}\n${debugContext}`;
return { prompt: enhancedPrompt, context };
};