diff --git a/apps/claude-trace/README.md b/apps/claude-trace/README.md index c07ac93..d069843 100644 --- a/apps/claude-trace/README.md +++ b/apps/claude-trace/README.md @@ -17,6 +17,9 @@ claude-trace # Include all API requests (by default, only substantial conversations are logged) claude-trace --include-all-requests +# Skip HTML generation (JSONL only, saves ~60% disk space) +claude-trace --no-html + # Run Claude with specific arguments claude-trace --run-with chat --model sonnet-3.5 diff --git a/apps/claude-trace/src/cli.ts b/apps/claude-trace/src/cli.ts index ad13e33..6dd24f4 100644 --- a/apps/claude-trace/src/cli.ts +++ b/apps/claude-trace/src/cli.ts @@ -35,6 +35,7 @@ ${colors.yellow}OPTIONS:${colors.reset} --run-with Pass all following arguments to Claude process --include-all-requests Include all requests made through fetch, otherwise only requests to v1/messages with more than 2 messages in the context --no-open Don't open generated HTML file in browser + --no-html Skip HTML generation (JSONL only) --log Specify custom log file base name (without extension) --claude-path Specify custom path to Claude binary --help, -h Show this help message @@ -234,6 +235,7 @@ async function runClaudeWithInterception( openInBrowser: boolean = false, customClaudePath?: string, logBaseName?: string, + generateHTML: boolean = true, ): Promise { log("Claude Trace", "blue"); log("Starting Claude with traffic logging", "yellow"); @@ -257,6 +259,7 @@ async function runClaudeWithInterception( NODE_OPTIONS: "--no-deprecation", CLAUDE_TRACE_INCLUDE_ALL_REQUESTS: includeAllRequests ? "true" : "false", CLAUDE_TRACE_OPEN_BROWSER: openInBrowser ? "true" : "false", + CLAUDE_TRACE_GENERATE_HTML: generateHTML ? "true" : "false", ...(logBaseName ? { CLAUDE_TRACE_LOG_NAME: logBaseName } : {}), }, stdio: "inherit", @@ -473,6 +476,9 @@ async function main(): Promise { // Check for no-open flag (inverted logic - open by default) const openInBrowser = !claudeTraceArgs.includes("--no-open"); + // Check for no-html flag (inverted logic - generate HTML by default) + const generateHTML = !claudeTraceArgs.includes("--no-html"); + // Check for custom Claude path let customClaudePath: string | undefined; const claudePathIndex = claudeTraceArgs.indexOf("--claude-path"); @@ -525,7 +531,7 @@ async function main(): Promise { } // Scenario 1: No args (or claude with args) -> launch claude with interception - await runClaudeWithInterception(claudeArgs, includeAllRequests, openInBrowser, customClaudePath, logBaseName); + await runClaudeWithInterception(claudeArgs, includeAllRequests, openInBrowser, customClaudePath, logBaseName, generateHTML); } main().catch((error) => { diff --git a/apps/claude-trace/src/interceptor.ts b/apps/claude-trace/src/interceptor.ts index 111c87f..64780d0 100644 --- a/apps/claude-trace/src/interceptor.ts +++ b/apps/claude-trace/src/interceptor.ts @@ -21,9 +21,12 @@ export class ClaudeTrafficLogger { private htmlGenerator: HTMLGenerator; constructor(config: InterceptorConfig = {}) { + // Check environment variable for HTML generation (defaults to true) + const enableHTMLFromEnv = process.env.CLAUDE_TRACE_GENERATE_HTML !== "false"; + this.config = { logDirectory: ".claude-trace", - enableRealTimeHTML: true, + enableRealTimeHTML: enableHTMLFromEnv, logLevel: "info", ...config, }; @@ -51,7 +54,9 @@ export class ClaudeTrafficLogger { // Output the actual filenames with absolute paths console.log(`Logs will be written to:`); console.log(` JSONL: ${path.resolve(this.logFile)}`); - console.log(` HTML: ${path.resolve(this.htmlFile)}`); + if (this.config.enableRealTimeHTML) { + console.log(` HTML: ${path.resolve(this.htmlFile)}`); + } } private isClaudeAPI(url: string | URL): boolean {