diff --git a/.env.example b/.env.example
index 962755bb..235a58a3 100644
--- a/.env.example
+++ b/.env.example
@@ -1,7 +1,22 @@
+# Sentry server-side DSN. Required for production builds, optional for local dev.
+# Obtain: Sentry project → Settings → Client Keys (DSN).
SENTRY_DSN=
+
+# Sentry browser DSN. Same value as SENTRY_DSN — Next.js exposes this to the client.
NEXT_PUBLIC_SENTRY_DSN=
+
+# Sentry CLI auth token. Required for source-map upload during `pnpm build`.
+# Obtain: Sentry → User Settings → Auth Tokens, scope `project:releases`.
SENTRY_AUTH_TOKEN=
+
+# Sentry environment label. Defaults to `development` locally; Vercel sets `production`.
SENTRY_ENVIRONMENT=development
NEXT_PUBLIC_SENTRY_ENVIRONMENT=development
+
+# Postgres connection string. Required for any feature that hits the DB
+# (view counters, reactions, dashboard). Use Neon or local Postgres for dev.
DATABASE_URL=
+
+# GitHub personal access token (read-only). Required by /api/github
+# (powers the GitHub stats card on the homepage). `public_repo` scope is sufficient.
GITHUB_TOKEN=
diff --git a/.git-hooks/pre-push b/.git-hooks/pre-push
index bc97dfe8..60bf9ba5 100755
--- a/.git-hooks/pre-push
+++ b/.git-hooks/pre-push
@@ -1,3 +1,2 @@
#!/usr/bin/env sh
-
-pnpm run pre-push
+exec pnpm run verify
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 00000000..579b67c9
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @Shramkoweb
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 00000000..a8386cac
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,66 @@
+name: Bug report
+description: Something on shramko.dev is broken or behaves unexpectedly
+labels: ['bug']
+body:
+ - type: textarea
+ id: summary
+ attributes:
+ label: Summary
+ description: What went wrong, in one or two sentences.
+ validations:
+ required: true
+ - type: textarea
+ id: reproduction
+ attributes:
+ label: Steps to reproduce
+ description: Numbered steps that someone else can follow to see the bug.
+ placeholder: |
+ 1. Go to https://shramko.dev/...
+ 2. Click ...
+ 3. See error
+ validations:
+ required: true
+ - type: textarea
+ id: expected
+ attributes:
+ label: Expected behavior
+ validations:
+ required: true
+ - type: textarea
+ id: actual
+ attributes:
+ label: Actual behavior
+ validations:
+ required: true
+ - type: input
+ id: browser
+ attributes:
+ label: Browser and version
+ placeholder: 'Chrome 130 / Safari 18 / Firefox 130'
+ validations:
+ required: true
+ - type: dropdown
+ id: viewport
+ attributes:
+ label: Viewport
+ options:
+ - Desktop
+ - Tablet
+ - Mobile
+ validations:
+ required: true
+ - type: dropdown
+ id: theme
+ attributes:
+ label: Theme
+ options:
+ - Light
+ - Dark
+ - System
+ validations:
+ required: true
+ - type: textarea
+ id: extra
+ attributes:
+ label: Anything else
+ description: Console errors, screenshots, related links.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 00000000..3ba13e0c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1 @@
+blank_issues_enabled: false
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 00000000..7b83f024
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,26 @@
+name: Feature request
+description: Suggest an addition or change
+labels: ['enhancement']
+body:
+ - type: textarea
+ id: problem
+ attributes:
+ label: Problem
+ description: What user need or pain point does this address?
+ validations:
+ required: true
+ - type: textarea
+ id: proposal
+ attributes:
+ label: Proposed solution
+ validations:
+ required: true
+ - type: textarea
+ id: alternatives
+ attributes:
+ label: Alternatives considered
+ description: Other approaches you thought about and why you chose the one above.
+ - type: textarea
+ id: extra
+ attributes:
+ label: Anything else
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000..b7cff9d6
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,33 @@
+## Summary
+
+
+
+-
+
+## Type
+
+
+
+- [ ] feat — new feature
+- [ ] fix — bug fix
+- [ ] docs — docs only
+- [ ] style — formatting / whitespace
+- [ ] refactor — internal restructuring
+- [ ] perf — performance
+- [ ] test — tests only
+- [ ] build — build system / deps
+- [ ] ci — CI config
+- [ ] chore — maintenance
+
+## Linked spec or plan
+
+
+
+## Test plan
+
+- [ ] `pnpm verify:full` exits 0 locally
+- [ ]
+
+## Breaking changes
+
+
diff --git a/.oxfmtrc.json b/.oxfmtrc.json
index 74643716..90218442 100644
--- a/.oxfmtrc.json
+++ b/.oxfmtrc.json
@@ -12,6 +12,7 @@
"build/**",
"coverage/**",
"prisma/migrations/**",
- "public/**"
+ "public/**",
+ "next-env.d.ts"
]
}
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 00000000..640e16a8
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,97 @@
+# AGENTS.md
+
+Brief for AI coding agents (Claude Code, Codex, Cursor, Copilot, Factory). Humans should read [README.md](README.md) first.
+
+## Project at a glance
+
+`shramko.dev` is a personal portfolio + blog + digital garden. Single deployment target: Vercel.
+
+| Layer | Tools |
+| ---------- | -------------------------------------------------------------- |
+| Framework | Next.js 16 (Pages Router), React 19 |
+| Language | TypeScript (strict) |
+| Styling | Tailwind CSS v4 |
+| Content | MDX via `next-mdx-remote`, Shiki for syntax highlighting |
+| Data | Prisma 7, Postgres (Neon in prod) |
+| Fetch | SWR |
+| Monitoring | Sentry, Vercel Analytics, Speed Insights, Checkly, UptimeRobot |
+| Testing | Jest + Testing Library |
+| Tooling | oxlint, oxfmt, commitlint, pnpm |
+
+## Setup
+
+Prereqs: Node 24.x (see `.nvmrc`), pnpm 10.x, Postgres database for any DB-touching feature.
+
+```bash
+pnpm install # also runs prisma generate via postinstall
+cp .env.example .env # each var is documented inline in .env.example
+pnpm dev # http://localhost:3000
+```
+
+## The verify loop
+
+**Two commands. Use the right one.**
+
+- `pnpm verify` — fast (~1 s). Runs `lint`, `format:check`, `typecheck`, `test:ci`. The pre-push hook calls this. Use during tight iteration.
+- `pnpm verify:full` — slower (~30–60 s). Adds `next build` on top. Matches what CI runs. **Run this before opening a PR.**
+
+**Success criterion:** before claiming work is done, `pnpm verify:full` exits 0.
+
+## Common tasks
+
+| Goal | Command |
+| ------------------------- | ------------------------------------- |
+| Start dev server | `pnpm dev` |
+| Run a single test file | `pnpm exec jest path/to/file.test.ts` |
+| Run a single test by name | `pnpm exec jest -t "test name"` |
+| Production build | `pnpm build` |
+| Format only (write) | `pnpm format` |
+| Lint only (auto-fix) | `pnpm lint:fix` |
+| Type check only | `pnpm typecheck` |
+| Scaffold a blog post | `pnpm article` |
+| Regenerate Prisma client | `pnpm exec prisma generate` |
+
+## Repo layout
+
+| Path | Purpose |
+| ------------------- | ---------------------------------------------------------- |
+| `pages/` | Next.js Pages Router routes + API endpoints (`pages/api/`) |
+| `components/` | Shared React components |
+| `lib/` | Utilities, schema, GitHub/Sentry helpers, scripts |
+| `_posts/` | Blog post MDX sources |
+| `_snippets/` | Code-snippet MDX sources |
+| `prisma/` | Prisma schema + migrations |
+| `public/` | Static assets |
+| `styles/` | Global CSS + Tailwind directives |
+| `__tests__/` | Page-level and API-route tests |
+| `__mocks__/` | Manual Jest mocks |
+| `docs/superpowers/` | Local-only specs and plans (gitignored) |
+
+## Conventions
+
+- **Commits:** [Conventional Commits](https://www.conventionalcommits.org/) Angular convention. Enforced by `commitlint.config.ts`. Lowercase subject, ≤ 50 chars, no period. Allowed types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`.
+- **Branches:** `feat/`, `fix/`, `chore/`, `ci/`, `docs/`, `refactor/`, `test/`, `perf/`.
+- **Formatting:** `oxfmt`. Pre-commit hook auto-formats staged JS/TS. Don't fight it.
+- **Before pushing:** the git pre-push hook (`.git-hooks/pre-push`) runs `pnpm verify` automatically. Don't bypass with `--no-verify`.
+- **Before opening a PR:** run `pnpm verify:full` (adds `build`). PR template has a checkbox for this.
+
+## Where things live
+
+- **Env vars** are documented in `.env.example` with descriptions and where to obtain each value.
+- **Secrets** live in Vercel project settings (`Sentry*`, `DATABASE_URL`, `GITHUB_TOKEN`). Read-only for agents — don't try to set them.
+- **The Sentry public DSN** (`NEXT_PUBLIC_SENTRY_DSN`) is intentionally exposed to the client; this is by Sentry design, not a leak.
+- **Specs and plans** for non-trivial work are in `docs/superpowers/specs/` and `docs/superpowers/plans/`. Both are gitignored — they live locally per-developer.
+
+## Don't do
+
+- Don't bypass git hooks with `--no-verify`.
+- Don't commit `.env` (only `.env.example`).
+- Don't hand-edit `pnpm-lock.yaml` — let `pnpm install` regenerate it.
+- Don't push directly to `main`. PRs only.
+- Don't add a dependency without a clear justification — this repo deliberately stays lean.
+- Don't write `*.md` files outside what an issue requires; this project does not use docs-as-features.
+
+## Notes for specific tools
+
+- **Claude Code:** `CLAUDE.md` at the repo root is a symlink to this file. Edit `AGENTS.md`, not `CLAUDE.md`.
+- **Pages Router / Sentry gap:** there is no `pages/_error.tsx` with `Sentry.captureUnderscoreErrorException`, so SSR errors from the Pages Router don't reach Sentry. Don't fix this inline as a side effect of unrelated work — open a focused PR for it.
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 120000
index 00000000..47dc3e3d
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1 @@
+AGENTS.md
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..4050ae76
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,46 @@
+# Contributing
+
+This is a personal, opinionated portfolio. Contributions are welcome but not the primary mode of development. If you're considering a non-trivial change, open an issue first to align on scope.
+
+## Setup
+
+See [README.md](README.md) for prerequisites and bootstrap steps.
+
+## Branch naming
+
+Use one of these prefixes (matches existing git history):
+
+- `feat/` — new feature
+- `fix/` — bug fix
+- `chore/` — maintenance / non-functional
+- `ci/` — CI configuration
+- `docs/` — documentation only
+- `refactor/` — internal restructuring
+- `test/` — tests only
+- `perf/` — performance work
+
+## Commits
+
+Conventional Commits (Angular convention) is enforced by `commitlint.config.ts`. Rules:
+
+- Lowercase subject, ≤ 50 characters, no trailing period.
+- Type required (one of: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`).
+- Imperative mood: `add`, not `added`.
+
+Examples: `feat: add OG image generator`, `fix(api): correct slug normalization`.
+
+## Pre-PR checks
+
+Run `pnpm verify:full` locally and confirm it exits 0. This runs:
+
+```
+oxlint → oxfmt --check → tsc --noEmit → jest --ci → next build
+```
+
+The PR template has a checkbox for this. Without it, CI will fail.
+
+## Pull request description
+
+- Explain the **why**, not just the what.
+- Flag breaking changes explicitly.
+- If the change has a spec under `docs/superpowers/specs/`, link it. Specs are local-only (gitignored), so paste the relevant excerpt rather than a path-only link.
diff --git a/README.md b/README.md
index 0853a328..c93ba9ee 100755
--- a/README.md
+++ b/README.md
@@ -10,8 +10,6 @@
-🇺🇦 Українською
-
## Contents
- [Tech Stack](#tech-stack)
@@ -61,15 +59,17 @@ App runs at http://localhost:3000.
## Scripts
-| Command | Purpose |
-| ----------------------------------- | -------------------------- |
-| `pnpm dev` | start dev server |
-| `pnpm build` | production build + sitemap |
-| `pnpm start` | start production server |
-| `pnpm lint` / `pnpm lint:fix` | oxlint |
-| `pnpm format` / `pnpm format:check` | oxfmt |
-| `pnpm test` / `pnpm test:coverage` | Jest |
-| `pnpm article` | scaffold a new blog post |
+| Command | Purpose |
+| ----------------------------------- | ------------------------------------------- |
+| `pnpm dev` | start dev server |
+| `pnpm build` | production build + sitemap |
+| `pnpm start` | start production server |
+| `pnpm lint` / `pnpm lint:fix` | oxlint |
+| `pnpm format` / `pnpm format:check` | oxfmt |
+| `pnpm test` / `pnpm test:coverage` | Jest |
+| `pnpm verify` | lint + format:check + typecheck + test (CI) |
+| `pnpm verify:full` | `verify` + production build |
+| `pnpm article` | scaffold a new blog post |
## Testing
@@ -93,7 +93,8 @@ Deploys automatically to [Vercel](https://vercel.com) on push to `main`. The `po
- Commits follow [Conventional Commits](https://www.conventionalcommits.org) (Angular convention). Enforced by `commitlint.config.ts`.
- A pre-commit hook auto-formats staged JS/TS via `oxfmt` (installed by `pnpm install` through the `prepare` script).
-- Run `pnpm pre-push` before pushing — it runs lint and format checks.
+- A pre-push hook (`.git-hooks/pre-push`) runs `pnpm verify` automatically before each push. Run `pnpm verify:full` (adds `next build`) before opening a PR.
+- See [AGENTS.md](AGENTS.md) if you're using an AI coding agent. Full policy in [CONTRIBUTING.md](CONTRIBUTING.md).
## License
diff --git a/README.ua.md b/README.ua.md
deleted file mode 100644
index 022aa500..00000000
--- a/README.ua.md
+++ /dev/null
@@ -1,100 +0,0 @@
-shramko.dev
-Портфоліо · блог · цифровий сад
-Дивитись сайт →
-
-
-
-
-
-
-
-
-
-English
-
-## Зміст
-
-- [Технології](#технології)
-- [Швидкий старт](#швидкий-старт)
-- [Команди](#команди)
-- [Тестування](#тестування)
-- [Деплой](#деплой)
-- [Моніторинг](#моніторинг)
-- [Внесок у проєкт](#внесок-у-проєкт)
-- [Ліцензія](#ліцензія)
-
-## Технології
-
-| Шар | Інструменти |
-| ------------- | -------------------------------------------------------------- |
-| Фреймворк | Next.js 16, React 19 |
-| Мова | TypeScript |
-| Стилі | Tailwind CSS v4 |
-| Контент | MDX, `next-mdx-remote`, Shiki |
-| Дані | Prisma 7, Postgres |
-| Стан / Запити | SWR |
-| Моніторинг | Sentry, Checkly, UptimeRobot, Vercel Analytics, Speed Insights |
-| Тести | Jest, Testing Library |
-| Інструменти | oxlint, oxfmt, commitlint, pnpm |
-| Хостинг | Vercel |
-
-## Швидкий старт
-
-### Передумови
-
-- Node 24.x
-- pnpm 10.x
-- База даних Postgres (локальна або хмарна, наприклад [Neon](https://neon.tech))
-
-### Установка
-
-```bash
-git clone https://github.com/Shramkoweb/Portfolio.git
-cd Portfolio
-pnpm install
-cp .env.example .env
-# заповніть DATABASE_URL, SENTRY_*, GITHUB_TOKEN
-pnpm dev
-```
-
-Застосунок запускається на http://localhost:3000.
-
-## Команди
-
-| Команда | Опис |
-| ----------------------------------- | ------------------------------- |
-| `pnpm dev` | запуск dev-сервера |
-| `pnpm build` | продакшн-збірка + sitemap |
-| `pnpm start` | запуск продакшн-сервера |
-| `pnpm lint` / `pnpm lint:fix` | oxlint |
-| `pnpm format` / `pnpm format:check` | oxfmt |
-| `pnpm test` / `pnpm test:coverage` | Jest |
-| `pnpm article` | створення шаблону нового допису |
-
-## Тестування
-
-Тести працюють на Jest з Testing Library. Запустіть `pnpm test` для повного набору або `pnpm test:coverage` для звіту покриття. Тести розташовані в `__tests__/`. CI завантажує покриття у Code Climate.
-
-## Деплой
-
-Автоматичний деплой на [Vercel](https://vercel.com) при пуші до `main`. Скрипт `postbuild` регенерує sitemap через `next-sitemap`. Pull request-и проходять перевірку Lighthouse budget, описану в `.github/workflows/lighthouse.yml`.
-
-## Моніторинг
-
-- [UptimeRobot status page](https://stats.uptimerobot.com/8lYYzuXNM9/792406216) — публічний моніторинг доступності сайту.
-
- 
-
-- [Checkly dashboard](https://portfolio-shramko.checkly-dashboards.com/?duration=30d) — E2E тести та статус збірки.
-
- 
-
-## Внесок у проєкт
-
-- Коміти йдуть у форматі [Conventional Commits](https://www.conventionalcommits.org) (Angular convention). Перевіряється через `commitlint.config.ts`.
-- Pre-commit хук автоматично форматує JS/TS через `oxfmt` (встановлюється `pnpm install` через скрипт `prepare`).
-- Запустіть `pnpm pre-push` перед пушем — він запускає lint і перевірку форматування.
-
-## Ліцензія
-
-MIT — дивіться [LICENSE](LICENSE).
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..27f83f63
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,33 @@
+# Security Policy
+
+## Supported versions
+
+Only the version currently deployed at is supported. There is no LTS branch.
+
+## Reporting a vulnerability
+
+Please **do not open a public GitHub issue** for security problems.
+
+Two reporting channels, in order of preference:
+
+1. **GitHub private vulnerability advisory** — preferred. Use the "Report a vulnerability" button on the [Security tab](https://github.com/Shramkoweb/Portfolio/security).
+2. **Email:** `shramko.dev@gmail.com` with subject `Security: `.
+
+## What to expect
+
+- Acknowledgement within 7 days (best-effort, solo maintainer).
+- Investigation timeline depends on severity. Critical issues that affect deployed code take priority.
+- Credit in the fix commit / release notes if you'd like (mention in your report).
+
+## Out of scope
+
+- Content under `_posts/` and `_snippets/` (not executable code).
+- Missing security headers on demo or staging subdomains.
+- Reports from automated scanners with no proof-of-concept.
+- Issues in third-party services I integrate with (Vercel, Sentry, GitHub) — please report to the upstream vendor.
+
+## Existing controls
+
+- GitHub native secret scanning is enabled (public repo, automatic).
+- CodeQL default setup is enabled (repo Security tab) — runs on every push and PR.
+- Dependencies are updated weekly via Renovate; `pnpm audit` runs in CI.
diff --git a/package.json b/package.json
index 65020157..736a1b33 100644
--- a/package.json
+++ b/package.json
@@ -15,12 +15,13 @@
"lint:fix": "oxlint --fix",
"postbuild": "next-sitemap --config next-sitemap.config.cjs",
"postinstall": "prisma generate",
- "pre-push": "pnpm run lint && pnpm run format:check && pnpm run typecheck && pnpm run test:ci",
"prepare": "git config core.hooksPath .git-hooks",
"start": "next start",
"test": "jest",
"test:ci": "jest --ci",
"test:coverage": "jest --coverage",
+ "verify": "pnpm run lint && pnpm run format:check && pnpm run typecheck && pnpm run test:ci",
+ "verify:full": "pnpm run verify && pnpm run build",
"typecheck": "tsc --noEmit"
},
"dependencies": {