A monorepo containing multiple Haskell applications and libraries, built with Bazel and featuring a NodeJS-based task management tool.
This repository is organized as a Bazel-based monorepo with the following structure:
- Haskell Applications (
haskell/app/
): Standalone executable applications - Haskell Libraries (
haskell/libs/
): Shared libraries and reusable components - Electron Applications (
electron-app/
): Cross-platform desktop applications with Haskell backends - Task Manager (
tools/mcp-shrimp-task-manager/
): TypeScript/NodeJS-based task management tool - Build System: Bazel with Stackage integration for Haskell dependency management
- Build System: Bazel
- Haskell: Managed via Stackage snapshots
- TypeScript/NodeJS: PNPM for dependency management
- Code Formatting: Fourmolu/Ormolu (Haskell), Prettier (TypeScript)
├── haskell/
│ ├── app/ # Haskell applications (executables)
│ └── libs/ # Haskell libraries (shared code)
├── electron-app/ # Electron desktop applications
│ └── aivika-population-viz/ # Population growth visualization
├── tools/
│ └── mcp-shrimp-task-manager/ # TypeScript task manager
├── MODULE.bazel # Bazel module configuration
├── WORKSPACE # Bazel workspace configuration
└── stackage_snapshot.json # Haskell dependency snapshot
- Bazel
- Haskell toolchain (managed by Bazel)
- Node.js and PNPM (for task manager)
- Clone the repository
- Install task manager dependencies:
bazel run -- @pnpm//:pnpm --dir $PWD/tools/mcp-shrimp-task-manager/ install
# Build all targets
bazel build //...
# Run all tests
bazel test //...
# Build a specific application
bazel build //haskell/app/<app-name>:<app-name>
# Run a specific application
bazel run //haskell/app/<app-name>:<app-name>
Haskell dependencies are managed through Stackage snapshots. To add a new dependency:
-
Add the package to
MODULE.bazel
:stack.package(name = "package-name")
-
Update the Stackage snapshot:
bazel run @stackage-unpinned//:pin -- --upgrade-hackage
-
Use the dependency in your
BUILD.bazel
:deps = ["@stackage//:package-name"]
For the task manager, dependencies are managed with PNPM:
cd tools/mcp-shrimp-task-manager
pnpm add <package-name>
- Use Fourmolu or Ormolu for formatting
- Follow Haddock documentation standards
- PascalCase for modules, types, and type classes
- camelCase for functions and variables
- Use Prettier for formatting
- Follow standard TypeScript naming conventions
- Group imports: external packages → project modules → relative imports
- Use Buildifier to format BUILD files
- Each application and library must have its own
BUILD.bazel
file
- Create directory:
haskell/app/<app-name>/
- Add
BUILD.bazel
withhaskell_binary
rule - Add source files (e.g.,
Main.hs
)
- Create directory:
haskell/libs/<lib-name>/
- Add
BUILD.bazel
withhaskell_library
rule - Organize source files by module structure
- Use the
scalpel
library for all web scraping - Respect
robots.txt
and set descriptive User-Agent headers - Write unit tests with local HTML fixtures
To install the dependencies for the task manager, run the following command from the root of the workspace:
bazel run -- @pnpm//:pnpm --dir $PWD/tools/mcp-shrimp-task-manager/ install
This project includes an interactive Electron application for visualizing population growth simulations powered by the Aivika system dynamics library.
- Real-time WebSocket Communication: Haskell backend with Electron frontend
- Interactive Visualization: Chart.js-powered population growth charts
- Customizable Parameters: Adjustable initial population, growth rate, and simulation time
- Multiple Views: Chart view, raw data table, and mathematical analysis
- Data Export: CSV and JSON export functionality
- Mathematical Model: Implements exponential growth (dP/dt = r × P)
To run the complete Aivika population growth visualization (both Haskell server and Electron frontend):
# Run the complete demo with both server and client
bazel run //electron-app/aivika-population-viz:aivika-demo
# Run in development mode with developer tools
bazel run //electron-app/aivika-population-viz:aivika-demo-dev
You can also run the components individually:
# Run just the Haskell backend in CLI mode
bazel run //haskell/app/aivika-population-growth:aivika-population-growth
# Run the Haskell backend in WebSocket server mode
bazel run //haskell/app/aivika-population-growth:aivika-population-growth -- --server --port 9161
# Run just the Electron visualization app (requires running server separately)
bazel run //electron-app/aivika-population-viz:aivika-population-viz
When running the Haskell backend in CLI mode, you can customize parameters:
INITIAL_POP=500 GROWTH_RATE=0.03 TIME_END=10 bazel run //haskell/app/aivika-population-growth:aivika-population-growth
To update and pin Haskell dependencies:
bazel run @stackage-unpinned//:pin
- Create a feature branch from
main
- Make changes following the code standards
- Update corresponding
BUILD.bazel
files - Run
bazel build //...
andbazel test //...
- Format code with appropriate formatters
- Commit and open a pull request
- DO NOT modify files in
bazel-*
directories - DO NOT use Cabal or Stack directly for Haskell package management
- DO NOT use npm or yarn in the task manager (use PNPM)
- DO NOT commit directly to the
main
branch - DO NOT modify
stackage_snapshot.json
manually