diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..0f243d7 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,5 @@ +{ + "enabledPlugins": { + "superpowers@claude-plugins-official": true + } +} diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 891b1a3..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,49 +0,0 @@ -# AGENTS.md - -This file provides guidance to AI Agents when working with code in this repository. - -## Commands - -```bash -npm run dev # Start development server -npm run build # Build for production -npm run lint # Run ESLint -npm run start # Start production server -``` - -No test suite is configured. - -## Architecture - -This is a **Next.js 14 App Router** portfolio site built with TypeScript, Tailwind CSS, and shadcn/ui. - -### Content Data Layer - -All portfolio content lives in a single file: `src/data/resume.tsx`. This exports a `DATA` object containing personal info, work experience, education, skills, projects, and social links. **This is the primary file to edit when updating portfolio content.** - -### Page Structure - -- `src/app/page.tsx` — Home page assembling all portfolio sections (hero, work, education, projects, skills, contact) -- `src/app/blog/page.tsx` — Blog listing -- `src/app/blog/[slug]/page.tsx` — Individual blog posts rendered from MDX -- `src/app/layout.tsx` — Root layout with theme providers and analytics - -### Blog System - -Blog posts are `.mdx` files in `content/`. The `src/data/blog.ts` utility handles parsing frontmatter (via `gray-matter`) and converting markdown to HTML using a remark/rehype pipeline with `rehype-pretty-code` (Shiki) for syntax highlighting. - -### Component Layers - -- `src/components/ui/` — shadcn/ui primitives (do not edit directly; regenerate via shadcn CLI) -- `src/components/magicui/` — Custom animation components (`BlurFade`, `BlurFadeText`, `Dock`) -- `src/components/` — Feature components (`ProjectCard`, `ResumeCard`, `HackathonCard`, etc.) - -### Styling - -- Tailwind CSS with HSL CSS variables for theming (defined in `src/app/globals.css`) -- Dark mode via `class` strategy with `next-themes` -- Theme-aware favicons handled by `src/components/favicon-switcher.tsx` - -### Path Alias - -`@/*` maps to `src/*` — use this for all internal imports. diff --git a/CLAUDE.md b/CLAUDE.md index 9cedc57..17fb39a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -47,3 +47,19 @@ Blog posts are `.mdx` files in `content/`. The `src/data/blog.ts` utility handle ### Path Alias `@/*` maps to `src/*` — use this for all internal imports. + +## Git Workflow & Releases + +Branch model: feature branches → `devel` → `main`. Both `devel` and `main` are protected by a repository ruleset (no direct pushes, no force-pushes, no branch deletion, PRs required). + +Versioning is fully automated by `semantic-release` (config in `release.config.js`, workflow in `.github/workflows/release.yml`), which only runs on pushes to `main` and reads commit messages via `@semantic-release/commit-analyzer` (Angular preset) to decide the version bump. + +**This means every PR into `devel` or `main` must be merged with "Squash and merge", never "Rebase and merge" or "Create a merge commit".** Rebase/merge-commit strategies leave multiple non-squashed commits (or, in the case of GitHub's rebase-and-merge, commits whose signature gets silently dropped on replay) in the history instead of one clean, conventional-commit-formatted entry — this has already caused a signature-verification incident on `devel` that had to be fixed by rewriting history. + +The PR title becomes the squash commit message and must follow [Conventional Commits](https://www.conventionalcommits.org/): +- `feat: ...` → minor bump +- `fix: ...` → patch bump +- `feat!: ...` or a `BREAKING CHANGE:` footer → major bump +- `chore:`, `docs:`, `refactor:`, etc. → no release + +`.github/workflows/ci.yml` (lint + build) runs on every PR targeting `main` or `devel`. diff --git a/README.md b/README.md index 10debb2..82c57b0 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ My personal portfolio website built with Next.js, showcasing my work experience, - [Tech Stack](#tech-stack) - [Getting Started](#getting-started) - [Commands](#commands) +- [Contributing](#contributing) - [License](#license) ## Tech Stack @@ -50,6 +51,22 @@ The development server starts at `http://localhost:3000`. | `npm run start` | Start production server | | `npm run lint` | Run ESLint | +## Contributing + +This repo uses [Conventional Commits](https://www.conventionalcommits.org/) and [semantic-release](https://semantic-release.gitbook.io/) to automate versioning and GitHub releases from `main`. + +1. Branch off `devel` (e.g. `feature/my-change`, `fix/my-bug`). +2. Open a PR into `devel`. The CI workflow (lint + build) runs automatically. +3. **Always merge with "Squash and merge"** — never "Rebase and merge" or "Create a merge commit". Every PR must land as a single commit so semantic-release can read the history correctly. +4. Give the PR a [Conventional Commits](https://www.conventionalcommits.org/) title, since it becomes the squash commit message: + - `feat: ...` → minor version bump + - `fix: ...` → patch version bump + - `feat!: ...` or a `BREAKING CHANGE:` footer → major version bump + - `chore:`, `docs:`, `refactor:`, etc. → no release +5. When `devel` is ready to ship, open a PR from `devel` into `main` and squash-merge it the same way. The push to `main` triggers the release workflow, which runs semantic-release to tag a version and publish a GitHub release from the commit history. + +`main` and `devel` are both protected branches: direct pushes, force-pushes, and branch deletion are blocked, and all changes must go through a reviewed PR. + ## License Licensed under the [Apache License, Version 2.0](LICENSE). diff --git a/package-lock.json b/package-lock.json index 76c6472..61ff4ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1573,7 +1573,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -1585,7 +1584,6 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -1641,7 +1639,6 @@ "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", @@ -2219,7 +2216,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3321,7 +3317,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -5162,7 +5157,6 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", - "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -6093,7 +6087,6 @@ "resolved": "https://registry.npmjs.org/next/-/next-14.2.35.tgz", "integrity": "sha512-KhYd2Hjt/O1/1aZVX3dCwGXM1QmOV4eNM2UTacK5gipDdPN/oHHK/4oVGy7X8GMfPMsUTUEmGlsy0EY1YGAkig==", "license": "MIT", - "peer": true, "dependencies": { "@next/env": "14.2.35", "@swc/helpers": "0.5.5", @@ -6580,7 +6573,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -6799,7 +6791,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -6812,7 +6803,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -7282,7 +7272,6 @@ "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.29.2.tgz", "integrity": "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg==", "license": "MIT", - "peer": true, "dependencies": { "@shikijs/core": "1.29.2", "@shikijs/engine-javascript": "1.29.2", @@ -7796,7 +7785,6 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -7945,7 +7933,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8146,7 +8133,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/public/scotiabank-logo.png b/public/scotiabank-logo.png new file mode 100644 index 0000000..4ad7c42 Binary files /dev/null and b/public/scotiabank-logo.png differ diff --git a/src/data/resume.tsx b/src/data/resume.tsx index 2aff230..4503f36 100644 --- a/src/data/resume.tsx +++ b/src/data/resume.tsx @@ -106,6 +106,17 @@ export const DATA = { }, }, work: [ + { + company: "Scotiabank", + href: "https://www.scotiabank.com/", + badges: [], + location: "Toronto, Ontario", + title: "AI Engineer", + logoUrl: "/scotiabank-logo.png", + start: "Jun. 2026", + end: "Present", + description: "NDA", + }, { company: "Microsoft", href: "https://www.microsoft.com/en-ca/", @@ -152,7 +163,7 @@ export const DATA = { title: "Undergraduate Researcher", logoUrl: "/ucalgary-logo.jpeg", start: "Sep. 2025", - end: "Present", + end: "Apr. 2026", description: "I am building BridgeMart, a cross-chain data marketplace on Ethereum and Solana where anyone can list, discover, and purchase encrypted datasets without trusting a third party. The core of the system is a privacy-preserving vending pipeline: datasets are encrypted client-side, integrity hashes are stored on-chain, and a smart-contract-gated decryption-key release ensures buyers only decrypt what they have paid for. On top of that, I shipped a RAG-style discovery service that uses LLM embeddings to match natural-language queries against tabular dataset signatures, backed by a FastAPI/Pydantic API and consumed by React and React Native frontends.", },