2025-11-17 15:36:49
As developers, we all reach a point when writing repetitive boilerplate stops being a minor annoyance and starts having a real impact on our productivity. You find yourself creating the same directory structure, writing the same configuration blocks, copying and pasting similar component patterns, then tweaking each instance slightly. What begins as a few minutes here and there quickly adds up to hours lost on manual scaffolding.
That's where this journey began for me. Initially, I built a collection of scripts to automate component creation for a side project. But the more I used those scripts, the more I realised this problem wasn't unique to my project or the framework I was using.
Today, Swarm is an extensible TypeScript code generation framework built on a plugin-based architecture. The first plugin I created solved my own needs for generating Wasp components, but the core library can be extended to generate code for any framework or platform.
So, dear developer, I'd like to share why Swarm exists, how it came to be and what it might offer you.
Before diving into Swarm itself, it's worth sharing the context that led to its creation. While researching framework options for a side project, I discovered Wasp, a full-stack web development framework, through their OpenSaaS starter template. I'd been looking at various paid SaaS starter templates at the time, but Wasp's offering stood out because it was free and included genuinely useful features out of the box.
I've been working with Wasp for almost a year now and in that time, I've become a contributor to the project and been fortunate enough to been recognised as a community expert. I believe the team behind it has built something with genuine potential. But as a relatively early (pre-1.0) project, I encountered certain friction points as I started using it more extensively.
Wasp gives developers considerable freedom in how they organise their projects. That flexibility can feel empowering, but it also imposes cognitive load as you have to decide on a structure yourself, often without clear guidance on what scales well. In my first project, I implemented a feature-based structure that segregated front- and back-end components into cohesive feature directories. Conceptually, I still stand by that decision, but there are definitely implementation details I'd approach differently now.
The configuration file presents another issue. Wasp uses a single configuration file, defined in their own DSL or TypeScript, to declare all server-side components. That includes RPC-style queries, actions (mutations), CRUD operations, background jobs, HTTP API endpoints, and page routes. The file offers a shallow learning curve and it's initially easy to manage, but it's monolithic and defines every component for the application. For complex app, it can grow very large and becomes increasingly difficult to navigate, even with careful organisation.
Then there's the server-side code structure. Wasp tends to recommend organising server operations in monolithic files like actions.ts or queries.ts. This works fine for small project, but as applications grow, these files can become unwieldy. More fundamentally, this pattern contrasts sharply with typical front-end conventions that define one component per file. That inconsistency creates mental overhead when switching between client and server code.
Swarm started out as a set of simple scripts I created to automate component creation for my first Wasp project. They worked well enough and I figured other Wasp developers might find them useful, so I decided to package them as a CLI tool that could easily be installed in other projects.
The next realisation was that MCP (Model Context Protocol) support would make these tools easier to use within AI-assisted development workflows. To enable that, I needed to break the core functionality out, with separate CLI and MCP packages, which forced me to think more carefully about the overall architecture.
Once I had both CLI and MCP working, another insight followed: this problem wasn't Wasp-specific. The same pattern of generating type-safe, context-aware boilerplate could apply to any framework. So I refactored everything into a technology-agnostic core handling both CLI command and MCP tool generation, with a separate plugin defining Wasp-specific generators.
Another revelation arrived when it struck me that CLI commands and MCP tools could be generated entirely from Zod schemas. I was already using Zod in a limited capacity throughout the application, but I realised I could define the schema once and get CLI flags, MCP tool parameters and validation for free, making plugin development much simpler.
While ironing out some compatibility bugs between the Zod 4 dependency in Swarm and the Zod 3 dependency in Wasp, I learned about Standard Schema and implemented support for that spec instead, allowing plugin schemas to be built with any library of the author's choice.
At the time of writing, the Swarm "ecosystem" consists of three main components:
As previously mentioned, the core framework is built around a plugin-based architecture that supports user-defined generators for different types of content, e.g. APIs, components, configuration or whatever makes sense for your use case. Generators and plugins implement simple, lightweight interfaces and leverage Standard Schema schemas for input validation.
export interface Generator<S extends StandardSchemaV1 = StandardSchemaV1> {
name: string;
description: string;
schema: S;
generate: (args: Out<S>) => Promise<void>;
}
export interface Plugin {
name: string;
providers: Array<GeneratorProvider>;
}
Those schemas drive both the command-line and MCP interfaces. You define a generator with a schema, and Swarm automatically exposes it as a command-line option and as an MCP tool that AI assistants can use, with no separate configuration or manual wiring required.
Swarm also includes a templating system built on Eta, with support for custom template overrides. If you need to tweak the output of a generator, you can place a modified template in a known location and the generator will use it in favour of the built-in version.
The Wasp plugin provides generators for all documented Wasp components. That includes API Endpoints and Namespaces, CRUD operations, Actions, Queries, Page Routes, and Background Jobs. All output is type-safe and compatible with your defined Prisma entities.
Beyond code generation, the plugin addresses those issues I mentioned earlier:
feature.wasp.ts files alongside the main main.wasp.ts. This contrasts with the traditional monolithic style, creating smaller, more manageable files and ensuring feature configuration is self-contained.App class that makes configuration files more concise and readable.The starter template demonstrates how to build Wasp applications using Swarm's code generation functionality. It provides a minimal implementation that's an ideal foundation for building custom starter templates on top of.
It includes modern tooling like Tailwind CSS 4 and shadcn/ui components, with several useful development scripts for common Wasp workflows. And of course, it includes Swarm's full MCP integration ready for AI-assisted development. If you're building a Wasp application and want to see how Swarm can accelerate your development workflow, give it a try!
This introduction covered the motivation, evolution, and current state of Swarm. In Part 2, I'll walk through creating a full-stack Wasp application using the Swarm toolkit. I'll show how the generators work in practice and how they can speed up development while maintaining type safety and consistency.
And if you're a Wasp developer dealing with some of the same friction points I encountered, I'd love to hear your thoughts.
2025-11-17 15:34:35
In Part 1, I shared the evolution of Swarm from a collection of project scripts into a full boilerplate generation framework. In this article, we'll walk through the steps of building a Wasp application using the Swarm Wasp Starter template, and I'll demonstrate how the generators work with real examples.
I'll be referencing the kitchen-sink example project from the Wasp repository throughout this article. It's a comprehensive example that features all major Wasp features, so it serves as a good reference point to understand how Swarm works.
Swarm implements a couple of notable changes to standard Wasp projects that it's useful to be aware of.
Wasp allows you to organise projects however you like, which is appealing at face-value. But when you're starting and scaling projects, that freedom comes with the cost of cognitive burden attached as you have to determine the best way to organise your files and directories.
Taking inspiration from Prettier, the Wasp plugin aims to remove all the mental effort of deciding how project files should be organised by enforcing a convention-driven, feature-based directory structure. Each feature directory is self-contained, with client and server subdirectories organised in a consistent structure, and a feature.wasp.ts configuration file that lives alongside the components it defines.
Here's what a typical feature directory looks like:
src/features/operations/
├── feature.wasp.ts
├── client/
│ └── pages/
│ ├── Serialization.tsx
│ ├── TaskDetail.tsx
│ └── Tasks.tsx
└── server/
├── actions/
│ ├── createTask.ts
│ ├── deleteCompletedTasks.ts
│ ├── toggleAllTasks.ts
│ └── updateTaskIsDone.ts
└── queries/
├── getNumTasks.ts
├── getTask.ts
└── getTasks.ts
This is in contrast to the standard guidance that recommends a single actions.ts file containing multiple actions, or a queries.ts file containing multiple queries. As the project grows, those files become harder to navigate and may ultimately get split up into sub-files, which again has an associated cost to plan and implement. The Wasp plugin's conventions dictate that each component lives in its own file, which also harmonises server-side components with client-side ones.
The main.wasp.ts file is still required, but it's much smaller as it purely handles application-level configuration like authentication and the root component. Feature-specific configuration now lives in feature.wasp.ts files, which keeps the main file manageable even as you add more features.
Wasp's declarative configuration syntax works fine, but it can be verbose. Swarm's extended App class provides a fluent API that makes configuration more concise and readable.
For example, typical Wasp declarations look like this:
app.route("tasks", {
path: "/tasks",
to: app.page("tasks", {
component: {
importDefault: "Tasks",
from: "@src/features/operations/client/pages/Tasks",
},
}),
});
app.crud("tasks", {
entity: "Task",
operations: {
create: {
overrideFn: {
import: "create",
from: "@src/features/crud/server/cruds/tasks" },
},
},
getAll: {
overrideFn: {
import: "getAll",
from: "@src/features/crud/server/cruds/tasks" },
},
},
},
);
While the equivalent in Swarm looks like this:
app
.addRoute("operations", "tasks", {
path: "/tasks",
auth: false
})
.addCrud("crud", "tasks", {
entity: "Task",
create: { override: true },
getAll: { override: true }
});
The fluent API lets generators chain these calls together, and Swarm organises them automatically so related declarations are grouped together and sorted alphabetically. It's a small detail, but it makes the configuration files much easier to read.
The generators automatically generate component files in consistent locations, following the plugin's structural conventions, so you don't have to define those yourself. The generated code is also fully type-safe, so if you reference an entity or property that doesn't exist in your Prisma schema, TypeScript will catch it.
The Swarm Wasp Starter template includes Swarm and the Wasp plugin, along with Tailwind CSS 4 and shadcn/ui components. You'll also find a collection of useful npm scripts for common Wasp workflows.
Run this command to create a new project using the starter:
npx @ingenyus/swarm create my-app --template genyus/swarm-wasp-starter
This clones the starter template and populates any templated values. Once installation is complete, navigate into the project directory and set it up:
npm run reset
The Swarm framework provides CLI commands for each of the generators defined by any active plugins, while the Wasp Starter template provides access to the CLI via the npm run swarm package script.
Let's say we want to replicate the kitchen-sink application, but built using Swarm.
The first step is to define any necessary entities in our schema.prisma file:
model User {
id Int @id @default(autoincrement())
isOnAfterSignupHookCalled Boolean @default(false)
isOnAfterLoginHookCalled Boolean @default(false)
isOnAfterEmailVerifiedHookCalled Boolean @default(false)
tasks Task[]
address String?
votes TaskVote[]
}
model Task {
id Int @id @default(autoincrement())
description String
isDone Boolean @default(false)
user User @relation(fields: [userId], references: [id])
userId Int
votes TaskVote[]
}
model TaskVote {
id String @id @default(cuid())
user User @relation(fields: [userId], references: [id])
userId Int
task Task @relation(fields: [taskId], references: [id])
taskId Int
}
Then we need to configure basic username authentication to enable auth settings:
main.wasp.ts
app.auth({
userEntity: "User",
methods: {
usernameAndPassword: {},
},
// Required, but not used in this tutorial
onAuthFailedRedirectTo: "/login",
});
Migrate the database and compile the project so Typescript is aware of the new entities:
wasp db migrate-dev --name init && wasp compile
Before generating any Wasp components, we must first generate a feature directory to contain them:
npm run swarm -- feature --target operations
All Swarm command arguments also support a short form, which you can review by using the -h argument to display the command help. The shortened form of the previous command would be:
npm run swarm -- feature -t operations
This creates the src/features/operations/ directory containing a bare feature.wasp.ts file, ready to start adding components.
feature.wasp.ts
import { App } from "@ingenyus/swarm-wasp";
export default function configureFeature(app: App, feature: string): void {
app
}
Next, we need to generate a login page (in the pre-existing root feature) and we'll also create a route for viewing tasks:
npm run swarm -- route --feature root --path /login --name login
npm run swarm -- route --feature operations --name tasks --path /tasks --auth
These commands generate page components at src/features/operations/client/pages/Tasks.tsx and src/features/root/client/pages/Login.tsx, then adds definitions to the respective feature.wasp.ts files. The --auth flag indicates the route requires authentication, which is reflected in the helper method call.
root/feature.wasp.ts
app
// Route definitions
.addRoute(feature, "login", {
path: "/login",
auth: false,
});
operations/feature.wasp.ts
app
// Route definitions
.addRoute(feature, "tasks", {
path: "/tasks",
auth: true,
});
Login.tsx
import React from "react";
export const Login = () => {
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-2xl font-bold mb-4">Login</h1>
{/* TODO: Add page content */}
</div>
);
};
Tasks.tsx
import React from "react";
export const Tasks = () => {
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-2xl font-bold mb-4">Tasks</h1>
{/* TODO: Add page content */}
</div>
);
};
Next, we can generate queries and actions for Task entity:
npm run swarm -- query --feature operations --operation getAll --data-type Task --name getTasks
npm run swarm -- action --feature operations --operation create --data-type Task --name createTask
These commands create boilerplate for the query and action handlers in src/features/operations/server/queries/getTasks.ts and src/features/operations/server/actions/createTask.ts respectively. The output is properly typed based on your Prisma schema, so you get full type-safety out of the box.
feature.wasp.ts
app
// Action definitions
.addAction(feature, "createTask", {
entities: ["Task"],
auth: false,
})
// Query definitions
.addQuery(feature, "getTasks", {
entities: ["Task"],
auth: false,
})
createTask.ts
import { Task } from "wasp/entities";
import { HttpError } from "wasp/server";
import type { CreateTask } from "wasp/server/operations";
export const createTask: CreateTask<Pick<Task, "description" | "userId"> & Partial<Pick<Task, "isDone">>> = async (data, context) => {
try {
const createdTask = await context.entities.Task.create({
data: {
...data,
}
});
return createdTask;
} catch (error) {
console.error("Failed to create Task:", error);
throw new HttpError(500, `Failed to create Task${error instanceof Error ? `: ${error.message}` : ''}`);
}
};
getTasks.ts
import { HttpError } from "wasp/server";
import type { GetAllTasks } from "wasp/server/operations";
export const getAllTasks = (async (_args, context) => {
try {
const tasks = await context.entities.Task.findMany();
return tasks;
} catch (error) {
console.error("Failed to get all tasks:", error);
if (error instanceof HttpError) {
throw error;
}
throw new HttpError(500, `Failed to get all tasks${error instanceof Error ? `: ${error.message}` : ''}`);
}
}) satisfies GetAllTasks<void>;
For CRUD operations, the kitchen-sink application offers a separate crud feature. Let's generate that and a set of operations:
npm run swarm -- feature --target crud
npm run swarm -- crud --feature crud --data-type Task --override get getAll create
This configures all the standard CRUD operations, with function overrides for the get, getAll and create operations. CRUD operations are a special case, with all overridden functions being exported from src/features/crud/server/cruds/tasks.ts.
feature.wasp.ts
app
// Crud definitions
.addCrud(feature, "tasks", {
entity: "Task",
get: {
override: true
},
getAll: {
override: true
},
create: {
override: true
},
update: {},
delete: {},
});
tasks.ts
import { type Task } from "wasp/entities";
import { HttpError } from "wasp/server";
import { type Tasks } from "wasp/server/crud";
export const getTask = (async ({ id }, context) => {
if (!context.user) {
throw new HttpError(401);
}
try {
const task = await context.entities.Task.findUnique({
where: { id },
});
if (!task) {
throw new HttpError(404, `task ${id} not found`);
}
return task;
} catch (error) {
console.error(`Failed to get task ${id}:`, error);
if (error instanceof HttpError) {
throw error;
}
throw new HttpError(500, `Failed to get task ${id}${error instanceof Error ? `: ${error.message}` : ''}`);
}
}) satisfies Tasks.GetQuery<Pick<Task, "id">>;
export const getAllTasks = (async (_args, context) => {
if (!context.user) {
throw new HttpError(401);
}
try {
const tasks = await context.entities.Task.findMany();
return tasks;
} catch (error) {
console.error("Failed to get all tasks:", error);
if (error instanceof HttpError) {
throw error;
}
throw new HttpError(500, `Failed to get all tasks${error instanceof Error ? `: ${error.message}` : ''}`);
}
}) satisfies Tasks.GetAllQuery<void>;
export const createTask: Tasks.CreateAction<Pick<Task, "description" | "userId"> & Partial<Pick<Task, "isDone">>> = async (data, context) => {
if (!context.user) {
throw new HttpError(401);
}
try {
const createdTask = await context.entities.Task.create({
data: {
...data,
}
});
return createdTask;
} catch (error) {
console.error("Failed to create Task:", error);
throw new HttpError(500, `Failed to create Task${error instanceof Error ? `: ${error.message}` : ''}`);
}
};
The kitchen-sink example includes various endpoints in the apis feature, so let's add one:
npm run swarm -- feature --target apis
npm run swarm -- api --feature apis --name fooBar --method ALL --path /foo/bar --entities Task
This creates the endpoint handler in src/features/apis/server/apis/fooBar.ts and registers it in your feature configuration. The generator handles the middleware setup and entity access automatically.
feature.wasp.ts
app
// Api definitions
.addApi(feature, "fooBar", {
method: "ALL",
route: "/foo/bar",
entities: ["Task"],
auth: false,
customMiddleware: true,
});
tasks.ts
import type { FooBar } from "wasp/server/api";
export const fooBar: FooBar = async (req, res, context) => {
// TODO: Implement your API logic here
res.json({ message: "OK" });
};
While the app doesn't yet have any proper business logic, all the boilerplate is valid code and you should be able to confirm the application runs by executing:
npm run dev
After the application opens, navigate to http://localhost:3000/tasks and you should be redirected to the /login page. The page component hasn't been completed yet, but simply verifies that the application can be compiled and executed successfully.
The CLI works well when you know exactly what you want to generate. Sometimes it's faster to just describe your intent and let an AI assistant figure out the right commands. That's where MCP comes in.
Start the MCP server with:
npm run swarm:mcp
Once you've configured your AI tool (Cursor, Claude Code, or VS Code Copilot) to connect to the server, you can use natural language prompts. Instead of remembering the exact CLI syntax, you can say something like:
"Create an operations feature with a route to view tasks, queries and actions for the Task entity, and an authenticated API endpoint."
The AI understands your intent and calls the appropriate MCP tools, which map directly to the CLI commands. You get the same generated code, but without having to construct the command yourself.
This becomes really powerful when you're building out a feature incrementally. You might start with a basic structure, then ask the AI to add more components as you think of them. Each request generates the right files in the right places, and everything stays consistent because it's all going through the same generators.
The MCP integration means Swarm's generators are available to any AI tool that supports the protocol. You're not locked into a specific editor or assistant—if it can talk MCP, it can use Swarm.
I've created a comparison video that shows a traditional Wasp application side-by-side with one built using Swarm Wasp Starter. Both are based on the kitchen-sink example, so you can see how the same functionality looks in each approach.
The video demonstrates the complete applications, highlighting the differences in structure, configuration, and development workflow. If you're curious about how Swarm changes the day-to-day experience of building Wasp apps, this should give you a clear picture.
If you're building with Wasp, give the Swarm Wasp Starter a try!
2025-11-17 15:33:10
By the end of this guide, you’ll be able to:
ls, cd, pwd, mkdir, rm, cp, mv)help and man pagesThe command line (terminal) is a place where you tell your computer what to do using text commands instead of clicking buttons.
You can open it by:
We will use general commands that work on macOS & Linux.
If you're on Windows and want the same commands, install Git Bash or use WSL. See the guide below.
Windows’ default terminals (Command Prompt, PowerShell) use different commands from macOS/Linux.
To make learning simpler, we’ll use Git Bash, which supports the same commands as macOS/Linux.
Choose “Windows” → Install with default settings.
After installation, open:
Git Bash
From here on, all commands in this guide will work the same whether you are on:
✔ Windows (Git Bash)
✔ macOS (Terminal)
✔ Linux (Terminal)
pwd
Means print working directory.
You’ll see something like:/Users/yourname
ls
cd Desktop
cd ..
cd ~
~ represent?mkdir test-folder
cd test-folder
Different OS options:
macOS / Linux:
touch notes.txt
Windows PowerShell:
ni notes.txt
Now check:
ls
cat notes.txt
(It will be empty; that’s fine.)
Run these commands:
mkdir projects
cd projects
mkdir project1
cd project1
touch index.html
touch script.js
cd ..
mkdir project2
Then explore with:
ls
cd project1
ls
pwd
A full address:
/Users/yourname/Desktop
Based on where you are:
cd ../other-folder
pwd
cd .. to climb back to your home folderTry examples:
cd /Users/<yourname>/Documents
Then:
cd ../Downloads
/?.. mean?⚠️ These commands are powerful. Practice on test files only.
mv oldname.txt newname.txt
mv notes.txt Documents/
cp notes.txt notes-copy.txt
rm notes-copy.txt
rm -r test-folder
Inside a temporary folder:
Commands typically follow this pattern:
command [options/flags] [arguments]
Examples:
ls -l
ls -a
rm -r foldername
Where:
Try:
ls -l
ls -a
ls -la
Observe the differences.
ls -a?Every command has built-in help.
man ls
Scroll with arrow keys, exit with q.
ls --help
Run:
man pwd
man mkdir
man rm
Or:
pwd --help
Here’s a realistic flow you’ll use constantly:
pwd
ls
cd foldername
ls
cat somefile.txt
cd ..
mkdir newfolder
mv file.txt newfolder/
rm oldfile.txt
Practice this every day and your comfort will skyrocket.
mkdir school
cd school
mkdir math english science
cd math
touch algebra.txt geometry.txt
cd ../english
touch essay1.txt
mv ../math/geometry.txt .
cp essay1.txt essay1-copy.txt
rm ../science
Try exploring the entire tree with:
ls -R
You can now:
2025-11-17 15:25:04
I made a Weddle-style NFL guessing game where you try to identify the mystery player in 8 attempts using clues like team, division, height, age, and position.
Looking for feedback on gameplay + suggestions for improvements.
Play here:
2025-11-17 15:21:11
2025-11-17 15:17:03
⚡ XGBoost finds non-obvious market drivers in 5 minutes instead of 3
weeks 📈 Prophet makes seasonal analysis so simple that product managers
can handle it 🔥 All models are already in production at top companies
(code examples inside)
You spend hours in Excel building forecasts that are outdated the moment
you present them. Your traditional time series models (ARIMA, I'm
looking at you) break down when the market sneezes.
Here's the brutal truth: While you're fighting with pivot tables,
competitors are using ML models that adapt in real-time.
I learned this the hard way when my Q4 demand forecast was off by
35%. The CMO wasn't happy. My weekend plans weren't either.
What it does: Predicts demand and prices by learning complex
patterns humans miss.
The stats:
import tensorflow as tf
from tensorflow.keras.layers import LSTM, Attention, Dense
import numpy as np
# Simple LSTM + Attention for demand forecasting
def build_lstm_attention_model(sequence_length, features):
inputs = tf.keras.Input(shape=(sequence_length, features))
# LSTM layer - captures temporal dependencies
lstm_out = LSTM(128, return_sequences=True)(inputs)
# Attention mechanism - focuses on important time steps
attention = Attention()([lstm_out, lstm_out])
# Combine and predict
flatten = tf.keras.layers.Flatten()(attention)
outputs = Dense(1, activation='linear')(flatten)
model = tf.keras.Model(inputs=inputs, outputs=outputs)
model.compile(optimizer='adam', loss='mse', metrics=['mae'])
return model
# Usage example
model = build_lstm_attention_model(sequence_length=30, features=5)
# Train on your data
# model.fit(X_train, y_train, epochs=50, batch_size=32)
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X_train)
What it does: Reveals which variables actually drive your market.
The stats:
import xgboost as xgb
from sklearn.model_selection import train_test_split
import pandas as pd
def analyze_market_drivers(df, target_column):
X = df.drop(columns=[target_column])
y = df[target_column]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
model = xgb.XGBRegressor(
n_estimators=100,
learning_rate=0.1,
max_depth=6,
subsample=0.8,
colsample_bytree=0.8,
random_state=42
)
model.fit(X_train, y_train)
importance_df = pd.DataFrame({
'feature': X.columns,
'importance': model.feature_importances_
}).sort_values('importance', ascending=False)
print("🔥 Top Market Drivers:")
print(importance_df.head(10))
return model, importance_df
What it does: Builds business-ready forecasts with holiday +
seasonality baked in.
The stats:
from prophet import Prophet
import pandas as pd
df = pd.DataFrame({
'ds': pd.date_range('2020-01-01', periods=365*3, freq='D'),
'y': your_sales_data
})
model = Prophet(
yearly_seasonality=True,
weekly_seasonality=True,
daily_seasonality=False,
changepoint_prior_scale=0.05
)
model.add_country_holidays(country_name='US')
model.fit(df)
future = model.make_future_dataframe(periods=365)
forecast = model.predict(future)
fig = model.plot(forecast)
fig2 = model.plot_components(forecast)
Model Accuracy Speed Explainability Difficulty
LSTM + Attention ⭐⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐⭐
XGBoost ⭐⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐
Prophet ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐⭐ ⭐