diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..047604a
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,7 @@
+BasedOnStyle: LLVM
+UseTab: Always
+TabWidth: 4
+IndentWidth: 4
+ColumnLimit: 100
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644
index 0000000..92c3c8e
--- /dev/null
+++ b/.clang-tidy
@@ -0,0 +1,12 @@
+Checks: >
+ clang-diagnostic-*,
+ clang-analyzer-*,
+ bugprone-*,
+ -bugprone-easily-swappable-parameters,
+ performance-*,
+ readability-*,
+ -readability-magic-numbers,
+ -readability-function-cognitive-complexity,
+ -readability-identifier-length,
+ -readability-braces-around-statements
+HeaderFilterRegex: '^(src|include)/.*'
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..a705607
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,36 @@
+name: C CI
+
+on:
+ push:
+ pull_request:
+ branches: [ "main" ]
+
+jobs:
+ build-and-test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y build-essential clang libcmocka-dev
+ sudo apt-get install -y build-essential clang clang-format clang-tidy libcmocka-dev bear
+
+ - name: Build
+ run: make CC=gcc
+
+ - name: Run tests
+ run: make test
+
+ - name: Check code formatting
+ run: |
+ find src/ -name '*.c' -o -name '*.h' | xargs clang-format --dry-run -Werror
+
+ - name: Generate compile_commands.json
+ run: bear -- make
+
+ - name: Static analysis
+ run: |
+ find src/ -name '*.c' | xargs clang-tidy --quiet
diff --git a/Makefile b/Makefile
index e69de29..db0064d 100644
--- a/Makefile
+++ b/Makefile
@@ -0,0 +1,33 @@
+CC = gcc
+CFLAGS = -std=c11 -Wall -Wextra -O2 -D_POSIX_C_SOURCE=199309L \
+-Iinclude -Ithird_party/stb
+LDFLAGS = -lm
+TARGET = conv
+VPATH = src src/lib
+SRCS = main.c conv.c cli.c filters.c
+
+OBJS = $(SRCS:.c=.o)
+
+LIB_OBJS = conv.o cli.o filters.o
+TEST_SRC = tests/test_conv.c
+TEST_BIN = test_runner
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+test: $(TEST_BIN)
+ ./$(TEST_BIN)
+
+$(TEST_BIN): $(LIB_OBJS) $(TEST_SRC)
+ $(CC) -std=c11 -Wall -Wextra -O0 -g -Iinclude -Ithird_party/stb \
+ $^ -o $@ -lm -lcmocka
+
+clean:
+ rm -f $(TARGET) $(OBJS) $(TEST_BIN)
+
+.PHONY: all test clean
diff --git a/README.md b/README.md
index e69de29..352d082 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,39 @@
+# Image Convolution — Task 1: Sequential
+
+Simple CLI tool for applying convolution filters images.
+
+## Build & Run
+
+```bash
+make # build
+./conv in.bmp out.bmp --filter sharpen # apply filter
+make test # run tests
+```
+
+## Usage
+
+```bash
+./conv