For developers. How Filesmith is put together, where things live, and how a change gets tested and shipped. User-facing command line docs are in cli.md.
| Process | Entry | Role |
|---|---|---|
| Main | src/main/index.ts |
Electron app, window, IPC handlers (src/main/ipc.ts), the job queue and every tool run. All privileged work happens here. |
| Preload | src/preload/index.ts |
contextBridge exposes the typed window.filesmith API. The renderer's only way into main. |
| Renderer | src/renderer/src/main.tsx |
React UI. Never touches fs or child_process. |
| CLI | src/cli/bootstrap.ts, built to out/main/cli.js |
The same engine as plain Node. The shims in resources\cli (filesmith.cmd, sh filesmith) run Filesmith.exe with ELECTRON_RUN_AS_NODE=1, so nothing the CLI imports may import electron. |
| CLI watchdog | src/cli/watchdog.ts, built to out/main/cliWatchdog.js |
Cleans up outputs and child tools after a CLI that was killed outright. |
| Console runner | src/main/console/ |
The bottom Console strip. Each command forks the bundled cli.js (runCli.ts); the line is re-validated in main (validate.ts) and only filesmith verbs run. Stop is staged: interrupt, interrupt again, then a tree kill. |
| Sidecars | src/main/pid/sidecar.ts, src/main/comfy/sidecar.ts |
Long-lived Python processes (PiD, spandrel) kept warm across images, talking newline-delimited JSON. |
The engine gets its host (userData, resources folder, downloads folder, fetch) from src/main/env.ts
(setEngineEnv). The app fills it from Electron, the CLI from Node. src/main/boot.ts holds startup
shared by both.
src/
main/ engine and Electron main process
tools/ one module per operation (convert, compress, resize, upscale, removebg,
pdf, archive), plus registry.ts (tool id -> module), plan.ts (dry-run
output names), estimate.ts, readiness.ts, soffice.ts, ncnnModels.ts
console/ Console strip: command catalog, validation, forked CLI runs
generate/ Generate: ComfyUI server, workflows, model scan, preflight
comfy/ ComfyUI discovery, imported upscalers, spandrel sidecar
pid/ PiD upscaler: GPU check, install, warm sidecar
rembg/ rembg install and paths
registry/ three-layer model registry (builtin, channel, user)
net/ downloads and SHA-256 integrity records
jobQueue.ts batch queue with concurrency limits and cancel
output.ts collision-safe output naming
atomicOutput.ts part files and placeholders for every output
toolResolver.ts finds bundled or PATH binaries
session.ts session.json load/save
preload/ typed window.filesmith bridge
renderer/src/ React UI: components/{shell,queue,inspector,options,console,views,ui,icons}
cli/ filesmith CLI: parse, plan, runner, events, commands/{doctor,files,formats,
generate,setup,skill}, watchdog
shared/ types and catalogs used by main, renderer and CLI (types.ts, ipc.ts,
tabs.ts, convert/compress/resize/removebg/archive/generate, console*)
- The renderer queues files per verb (
src/shared/tabs.ts) and callsjob:runthrough the preload bridge with aJobRequest. JobQueue(src/main/jobQueue.ts) runs jobs with a global concurrency limit and per-tool caps (upscale and removebg run one at a time). Each job gets anAbortControllerfor cancel.getTool()insrc/main/tools/registry.tspicks the tool module, which builds the arguments and spawns the CLI tool throughsrc/main/run.ts.- Progress goes back as
JobEvents (status,percent,etaSec,outputPath,outputSize,error) broadcast on thejob:eventIPC channel. - Outputs are atomic (
src/main/atomicOutput.ts): the final name is reserved first with an empty placeholder (exclusive create), the tool writes a.filesmith-partsibling, and the part is renamed onto the final name only on success. A failed or canceled job leaves no half-written file. - Names are collision-safe (
src/main/output.ts, ported from RCMM):name.ext, thenname (tag).ext,name (tag 2).ext, andbase (2)for folders. A source or existing file is never overwritten.tools/plan.tspredicts the same names for dry runs.
The CLI runs the same tool modules through src/cli/runner.ts, not the app's queue, so CLI jobs never
show up in the app.
- Core tools ship in the installer: ffmpeg, ImageMagick, mutool, CaesiumCLT and 7-Zip in
resources\bin, plusresources\libreoffice,resources\ghostscriptandresources\realesrgan. They are not in git. npm run binaries(scripts/fetch-binaries.mjs) stages them, mostly from local installs.--pinned(used by CI) downloads the same versions from official sources, each checked against a SHA-256 inscripts/pinned-tools.mjs.scripts/verify-bundle.mjschecks every tool is present and runs.- WinRAR's
Rar.execannot be bundled. It is detected at runtime and RAR/CBR targets are greyed out without it.
- Real-ESRGAN (ncnn/Vulkan) is bundled. Extra models download into
%APPDATA%\Filesmith\models. - rembg, PiD and spandrel install on demand with
uvinto%APPDATA%\Filesmith(uv,uv-tools,pid,models). The app offers setup in the UI; the CLI never downloads during a job and saysRun: filesmith setup <tool>instead. - Generate drives a local ComfyUI (
filesmith setup comfy --folderor--url). - Model downloads come from the registry:
resources\registry\*.json(builtin), a signed channel layer, and the user layer in%APPDATA%\Filesmith\registry\user, which updates never touch.
- userData is
%APPDATA%\Filesmith, shared by the app and the CLI.FILESMITH_USER_DATAoverrides it (tests and CI). session.jsonholds queues, results and options (written atomically).- UI preferences (sidebar order and hidden verbs, sidebar collapsed, view size, console panel) live in
renderer
localStorage. - Other files there:
integrity.json,comfy-upscalers.json,comfy-live.json,locks\.
- Unit:
npm test(Vitest,test\*.test.ts).electronis stubbed bytest/mocks/electron.ts. Covers argument builders, catalogs, output naming, CLI parsing, IPC parity, and a no-em-dash check. - E2E:
npm run build, thennpm run test:e2e(Playwright_electron, specs ine2e\). The app runs hidden (FILESMITH_E2E_HIDDEN=1frome2e/helpers.ts), one worker at a time. - Screenshots: set
FILESMITH_SHOTS=1to capture shots (for examplee2e/visual.spec.tswrites todocs\mockups\terminal-v5\shots). They are evidence for design review, not assertions. - E2E is the local pre-PR gate; CI does not run it.
ci.yml(PRs and pushes to main,windows-latest): typecheck, lint,prettier --check, unit tests.release.yml(push to main, except docs-only changes): the same gates, then a check thatv<version>frompackage.jsondoes not exist yet, pinned tool fetch, bundle verification before and after packing, a smoke test of the packed CLI, andgh release createwith generated notes. Output:Filesmith-Setup-x64-<version>.exeplus a stable-named copy. Unsigned.- PRs that touch release machinery (
package.json, scripts,src/cli, installer files) get a dry run: the installer is built and uploaded as a workflow artifact, never published. Bump the version in the PR, or the dry run fails.
The look is designed with the owner. Make no visual assumptions: any UI change starts with
self-contained HTML mockups and waits for sign-off. The signed-off target is
docs\mockups\terminal-v5\10-s2-vscode-grouped.html (dark only, strict monochrome). The rules and
feedback trail are in design/redesign-direction.md. Feature specs live in
docs\superpowers\specs.