Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion apps/claude-trace/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ claude-trace --include-all-requests
# Run Claude with specific arguments
claude-trace --run-with chat --model sonnet-3.5

# Use custom output directory (default: .claude-trace)
claude-trace --output-dir /path/to/logs

# Combine custom output directory with custom log name
claude-trace --output-dir ~/my-traces --log my-session

# Show help
claude-trace --help

Expand All @@ -36,7 +42,7 @@ claude-trace --generate-html logs.jsonl --include-all-requests
claude-trace --index
```

Logs are saved to `.claude-trace/log-YYYY-MM-DD-HH-MM-SS.{jsonl,html}` in your current directory. The HTML file is self-contained and opens in any browser without needing a server.
Logs are saved to `.claude-trace/log-YYYY-MM-DD-HH-MM-SS.{jsonl,html}` in your current directory (or the directory specified by `--output-dir`). The HTML file is self-contained and opens in any browser without needing a server.

## Request Filtering

Expand Down
19 changes: 18 additions & 1 deletion apps/claude-trace/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ ${colors.yellow}OPTIONS:${colors.reset}
--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
--log Specify custom log file base name (without extension)
--output-dir Specify custom output directory (default: .claude-trace)
--claude-path Specify custom path to Claude binary
--help, -h Show this help message

Expand Down Expand Up @@ -92,9 +93,16 @@ ${colors.yellow}EXAMPLES:${colors.reset}
# Use custom Claude binary path
claude-trace --claude-path /usr/local/bin/claude

# Use custom output directory
claude-trace --output-dir /path/to/logs

# Combine custom output directory with custom log name
claude-trace --output-dir ~/my-traces --log my-session

${colors.yellow}OUTPUT:${colors.reset}
Logs are saved to: ${colors.green}.claude-trace/log-YYYY-MM-DD-HH-MM-SS.{jsonl,html}${colors.reset}
With --log NAME: ${colors.green}.claude-trace/NAME.{jsonl,html}${colors.reset}
With --output-dir: ${colors.green}<output-dir>/log-YYYY-MM-DD-HH-MM-SS.{jsonl,html}${colors.reset}

${colors.yellow}MIGRATION:${colors.reset}
This tool replaces Python-based claude-logger and claude-token.py scripts
Expand Down Expand Up @@ -234,6 +242,7 @@ async function runClaudeWithInterception(
openInBrowser: boolean = false,
customClaudePath?: string,
logBaseName?: string,
outputDir?: string,
): Promise<void> {
log("Claude Trace", "blue");
log("Starting Claude with traffic logging", "yellow");
Expand All @@ -258,6 +267,7 @@ async function runClaudeWithInterception(
CLAUDE_TRACE_INCLUDE_ALL_REQUESTS: includeAllRequests ? "true" : "false",
CLAUDE_TRACE_OPEN_BROWSER: openInBrowser ? "true" : "false",
...(logBaseName ? { CLAUDE_TRACE_LOG_NAME: logBaseName } : {}),
...(outputDir ? { CLAUDE_TRACE_OUTPUT_DIR: outputDir } : {}),
},
stdio: "inherit",
cwd: process.cwd(),
Expand Down Expand Up @@ -487,6 +497,13 @@ async function main(): Promise<void> {
logBaseName = claudeTraceArgs[logIndex + 1];
}

// Check for custom output directory
let outputDir: string | undefined;
const outputDirIndex = claudeTraceArgs.indexOf("--output-dir");
if (outputDirIndex !== -1 && claudeTraceArgs[outputDirIndex + 1]) {
outputDir = claudeTraceArgs[outputDirIndex + 1];
}

// Scenario 2: --extract-token
if (claudeTraceArgs.includes("--extract-token")) {
await extractToken(customClaudePath);
Expand Down Expand Up @@ -525,7 +542,7 @@ async function main(): Promise<void> {
}

// 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, outputDir);
}

main().catch((error) => {
Expand Down
10 changes: 8 additions & 2 deletions apps/claude-trace/src/interceptor-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ try {
const jsPath = path.join(__dirname, "interceptor.js");
const tsPath = path.join(__dirname, "interceptor.ts");

// Build config from environment variables
const config = {};
if (process.env.CLAUDE_TRACE_OUTPUT_DIR) {
config.logDirectory = process.env.CLAUDE_TRACE_OUTPUT_DIR;
}

if (fs.existsSync(jsPath)) {
// Use compiled JavaScript
const { initializeInterceptor } = require("./interceptor.js");
initializeInterceptor();
initializeInterceptor(config);
} else if (fs.existsSync(tsPath)) {
// Use TypeScript via tsx
require("tsx/cjs/api").register();
const { initializeInterceptor } = require("./interceptor.ts");
initializeInterceptor();
initializeInterceptor(config);
} else {
console.error("Could not find interceptor file");
process.exit(1);
Expand Down