This document helps Claude understand the Sleuth repo structure and common development patterns.
Sleuth is an Electron-based Slack Log Viewer application built with React, TypeScript, and MobX for state management. It processes various types of log files (browser, webapp, mobile, installer, etc.) and provides a comprehensive UI for viewing, filtering, and analyzing log data.
IMPORTANT: Never attempt to run npm, instead always run the equivalent yarn command
yarn install- Install dependenciesyarn start- Start development build with Electronyarn test- Run tests with Vitestyarn run tsc- TypeScript type checkingyarn run lint- Run linting (oxlint and oxfmt)yarn run lint:fix- Fix linting issues automatically
Note that you should avoid running these commands as they are slow. Instead prefer running the above Core Commands to validate changes rapidly.
yarn run package- Package the app for current platformyarn run make- Create distributable for current platformyarn run publish- Publish to configured distributors
yarn run catapult:update- Update the catapult git submodule
The app follows standard Electron patterns with three main processes:
- Main Process (
src/main/index.ts): Application lifecycle, window management, IPC handling, file system operations - Renderer Process (
src/renderer/renderer.ts): React UI rendering - Preload Script (
src/preload/preload.ts): Secure bridge exposingwindow.SleuthAPI to renderer
- SleuthState (
src/renderer/state/sleuth.ts): Main MobX store managing log files, selected entries, filters, bookmarks, and UI state - Uses MobX observables with React integration via
mobx-react
- Processor (
src/renderer/processor.ts): Core log file parsing and processing logic - Handles multiple log types: browser, webapp, mobile, installer, chromium, netlog, trace, state
- Converts raw log files into structured
LogEntryobjects with timestamps, levels, and metadata
- App (
src/renderer/components/app.tsx): Root component - AppCore (
src/renderer/components/app-core.tsx): Main application layout - LogTable (
src/renderer/components/log-table.tsx): Virtualized log entry display - Sidebar (
src/renderer/components/sidebar/): File tree, bookmarks, and navigation - Preferences (
src/renderer/components/preferences/): Settings and configuration
- LogEntry (
src/interfaces.ts): Core data structure for log entries - UnzippedFile/ProcessedLogFile/MergedLogFile: File handling abstractions
- LogType enum: Defines supported log file types
- IpcEvents (
src/ipc-events.ts): Defines all IPC channel constants - IPC Manager (
src/main/ipc.ts): Handles main process IPC routing - SleuthAPI (preload): Exposed renderer API with type safety
The core flow for processing log bundles:
- Reception: Files enter via drag-drop (
app.tsx), OS file open (app.on('open-file')inindex.ts), or IPC. All paths callwindow.Sleuth.openFile(url). - Unzipping (
src/main/unzip.ts):Unzipperclass usesyauzl(lazy mode) to extract to a temp directory, returningUnzippedFile[]. System files filtered bysrc/utils/should-ignore-file.ts. - Type categorization (
src/utils/get-file-types.ts):getTypesForFiles()classifies each file by filename pattern intoLogType(BROWSER, WEBAPP, NETLOG, INSTALLER, MOBILE, CHROMIUM, STATE, TRACE, UNKNOWN). - Routing (
app-core.tsx): Files split into raw files (STATE, NETLOG, TRACE — displayed as-is) and processable files (BROWSER, WEBAPP, INSTALLER, MOBILE, CHROMIUM — parsed line-by-line). - State files read first:
log-context.jsonprovides timezone info used during log parsing. - Line-by-line parsing (
src/main/filesystem/read-file.ts):readLogFile()streams each file, applying type-specific regex matchers (e.g.,matchLineElectron,matchLineWebApp). Each matched line becomes aLogEntrywith timestamp, level, message, and metadata. Repeated consecutive lines are collapsed. - Merging & sorting (
src/renderer/processor.ts):mergeLogFiles()combines entries from multiple files of the same type using a synchronous k-way merge (sorted chronologically). Produces merged views for Browser, Webapp, and combined ALL. - Display: Results stored in MobX
SleuthState, populating Sidebar (file tree), LogTable (virtualized entries), and detail panels.
Key file dispatch: src/main/filesystem/open-file.ts routes between openZip(), openDirectory(), and openSingleFile().
- Trace Analysis: Chrome DevTools and Perfetto trace file support
- State Inspection: Redux state visualization
- NetLog Viewing: Network log analysis
- Bookmarking: Save and restore specific log entries
- Log Merging: Combine multiple log files chronologically
- Uses Vitest with jsdom environment
- Test files:
test/**/*.test.[jt]s?(x) - Benchmark files:
test/bench/*.bench.ts— run withyarn vitest bench- Real-log benchmarks require
SLEUTH_BENCH_LOGS=/path/to/extracted/logs
- Real-log benchmarks require
- Setup:
test/vitest-setup.js - Global test utilities available
- Linting is handled by oxlint (configured in
.oxlintrc.json) - Strict separation: renderer code cannot import Node.js modules (fs, path, etc.)
- Uses
typescriptandreactoxlint plugins for TypeScript and React-specific rules - Unused vars prefixed with
_are ignored - Disable comments use
// oxlint-disable-next-lineformat
src/main/: Main process Electron codesrc/renderer/: React renderer codesrc/preload/: Preload script for secure IPCsrc/utils/: Shared utilities (renderer-safe only)src/interfaces.ts: Shared TypeScript interfaces
- Use MobX
@observableand@actiondecorators - State mutations only through actions
- Computed values for derived data
- Autorun for side effects
IMPORTANT: never disable a lint rule or add @ts-ignore unless explicitly asked to do so by the user. If you are unable to solve a problem without disabling a lint rule, stop and ask the user for guidance first.
CRITICAL: NEVER use --no-verify flag with git commands. This bypasses pre-commit hooks that enforce code quality and can introduce broken code into the repository. Always fix the underlying issues that cause hooks to fail instead of bypassing them. If a hook is failing, address the specific linting, formatting, or test failures it identifies.
- Context isolation enabled - use
contextBridgein preload - Renderer process has restricted Node.js access
- File operations must go through IPC to main process
- Log table uses
react-virtualizedfor large datasets - Log processing happens in chunks to avoid blocking UI
- Per-type file processing runs in parallel (
Promise.allSettledinapp-core.tsx) mergeLogFilesuses a synchronous k-way merge (O(n·k), avoids concat+sort)- Timestamp parsing uses
toTZMillis()with a cached TZ offset per calendar date, avoiding expensiveTZDateconstruction (~7µs) on every line — the single biggest bottleneck in log processing - The main processing hot path is in
src/main/filesystem/read-file.ts: regex matching → timestamp parsing → LogEntry creation
- Cross-platform Electron app (macOS, Windows, Linux)
- Platform-specific code paths handled via
process.platform