One Thing per Folder
The codebase follows one rule: one thing per folder, file matches folder name .
Top-Level Layout
src/
├── main/ Electron main process
│ ├── index.ts IPC handlers — the entry point
│ ├── channels/ Telegram, WhatsApp, Electron
│ ├── runtime/ The 15-module brain
│ ├── workspace/ ~/.wolffish management
│ ├── conversations/ SQLite conversation store
│ ├── uploads/ File processing
│ └── {service}/ github/, google/, notion/, ollama/, etc.
├── preload/ contextBridge (IPC types)
├── renderer/src/ React frontend
│ ├── pages/ One folder per screen
│ ├── components/ core/ (primitives) + common/ (composed)
│ ├── providers/ React context (Theme, Locale, Flow)
│ ├── hooks/ Custom hooks
│ └── lib/ i18n, utils
└── defaults/workspace/ Bundled brain (copied on first launch)
Folder Convention
Every folder contains exactly one thing. The primary file matches the folder name:
components/core/copy-button/CopyButton.tsx
hooks/use-online/useOnline.ts
main/runtime/hippocampus/hippocampus.ts
main/runtime/corpus/corpus.ts
pages/chat/Chat.tsx
providers/theme/ThemeProvider.tsx
No barrel index.ts files. Imports always use the explicit file path via aliases:
// Explicit path — always know exactly what you're importing
import { CopyButton } from '@components/core/copy-button/CopyButton'
import { useOnline } from '@hooks/use-online/useOnline'
Provider/Hook Split Pattern
When a file has both a React component and a hook, split them into separate files. This is required for React Fast Refresh to work correctly.
providers/flow/
├── FlowProvider.tsx # Component only (renders context provider)
└── useFlow.ts # Context creation + hook + types
// useFlow.ts — context, hook, and types
import { createContext , useContext } from 'react'
export interface FlowContextValue { /* ... */ }
export const FlowContext = createContext < FlowContextValue | null >( null )
export function useFlow () : FlowContextValue {
const ctx = useContext ( FlowContext )
if ( ! ctx ) throw new Error ( 'useFlow must be used within FlowProvider' )
return ctx
}
// FlowProvider.tsx — component only
import { FlowContext , FlowContextValue } from './useFlow'
export function FlowProvider ({ children }) {
const value : FlowContextValue = { /* ... */ }
return < FlowContext . Provider value ={ value }>{ children } </ FlowContext . Provider >
}
Fast Refresh only works when a file exports components OR hooks, not both. Mixing them causes full-page reloads during development.
The Runtime Folder
src/main/runtime/
├── agent.ts The pipeline orchestrator
├── corpus/ Event bus (mitt-based)
├── thalamus/ LLM provider routing
├── prefrontal/ Context assembly
├── ras/ Attention filtering
├── cortex/ Fast retrieval (FTS5)
├── hippocampus/ Conversation memory
├── cerebellum/ Capability discovery
├── wernicke/ Output parsing
├── broca/ Response streaming
├── amygdala/ Safety gate
├── motor/ Task execution
├── basalganglia/ Outcome recording
├── hypothalamus/ System health
├── brainstem/ Background tasks
└── insula/ Status introspection
Each module is a class with a constructor that accepts { workspaceRoot, corpus, ... }. Modules never import each other — they communicate through corpus events.
Data Location
~/.wolffish/
├── workspace/
│ ├── brain/ Runtime state (skills, memory, logs)
│ ├── soul.md System personality
│ ├── user.md User context
│ ├── agents.md Agent definitions
│ └── config.json Runtime configuration
├── conversations.db SQLite conversation store
└── uploads/ Processed files
~/.wolffish/ is the ENTIRE footprint. The app stores nothing elsewhere. Uninstall is rm -rf ~/.wolffish/ plus removing the app itself.
Workspace Initialization
The defaults/workspace/ directory in the source tree is the template. On first launch:
Wolffish checks if ~/.wolffish/workspace/ exists
If it doesn’t, copies defaults/workspace/ into ~/.wolffish/workspace/
If it does, nothing happens — user data is never overwritten
Workspace init runs ONLY when ~/.wolffish/workspace/ doesn’t exist. If you add new default files to defaults/workspace/, existing users won’t get them automatically. Handle migrations in code if needed.
Development Setup Get the dev environment running.
Adding Brain Modules Add a new module to the runtime pipeline.