A personal portfolio website built with Astro and MDX, with an embedded CLI-style assistant that answers questions using page-scoped context.
This project is Alfredo Navas' portfolio and knowledge site. It combines:
- static-first, SEO-friendly pages
- a content-driven blog and profile knowledge base
- a lightweight AI chat entry point for natural-language questions
The site is optimized for fast rendering, predictable content loading, and maintainable content operations.
- Astro for page routing and rendering
- MDX for long-form content
- Astro Content Collections for typed blog content
- Netlify adapter for server output and API runtime
- OpenAI Responses API for AI answers (
gpt-4.1-mini)
The chat flow is intentionally minimal and page-aware:
- The CLI form in
BaseLayoutsends a POST request tosrc/pages/api/ask.ts. - The API validates input, applies a short per-IP cooldown, and normalizes payloads.
loadContext(src/lib/context.ts) builds context from:/content/knowledge/*.mdxglobal knowledge files- a generated blog discovery index (
src/lib/blog-context.ts) - optional current-page context (when provided by the route)
askAI(src/lib/ai.ts) sends the constrained prompt and context to the OpenAI Responses API.- The API returns concise text for direct rendering in the CLI message thread.
- context is scoped to what the question needs
- no full-site content dump is sent by default
- deterministic command-like behavior remains local; AI is for free-form answers
- Source:
/content/knowledge/*.mdx - Purpose: profile, skills, experience, tools, and background context for the assistant
- Source:
/src/content/blog/*.mdx - Config:
src/content.config.ts - Listing:
/blog/ - Detail routes:
/blog/[slug]/via static paths
Blog posts support frontmatter fields including slug, category, tags, dates, and optional hero images.
.
├── content/
│ └── knowledge/ # AI knowledge base (MDX)
├── public/ # Static assets
├── src/
│ ├── assets/ # Images/icons
│ ├── components/ # Reusable UI blocks
│ ├── content/ # Blog MD/MDX source
│ ├── layouts/ # Shared layouts + CLI shell
│ ├── lib/ # AI/context utilities
│ ├── pages/ # Astro routes + API handlers
│ │ └── api/ask.ts # AI endpoint
│ └── styles/ # Global styles and tokens
├── astro.config.mjs # Astro + Netlify adapter config
└── package.json
- Node.js
>= 22.12.0 - npm
npm installnpm run devThe app runs on the default Astro dev host (typically http://localhost:4321).
Create a local .env file (or set variables in your shell):
ELPUAS_OPENAI_API_KEY=your_openai_api_keyELPUAS_OPENAI_API_KEY: used server-side bysrc/lib/ai.tsfor OpenAI Responses API calls
Without this variable, /api/ask cannot generate answers.
npm run buildnpm run preview- Astro is configured with
output: 'server'and@astrojs/netlifyinastro.config.mjs. - Ensure
ELPUAS_OPENAI_API_KEYis set in Netlify environment variables. - Contact form (
src/pages/contact.astro) uses Netlify Forms markup (data-netlify="true"). - API route
/api/askruns in server context; it is not prerendered.
- CLI-style chat panel available across the site
- contextual answering that can include current page context (especially blog posts)
- automatic link parsing in assistant messages for internal/external references
- responsive mobile sidebar with accessible toggle and Escape/outside-click close behavior
- profile popover helper with keyboard/outside-click dismissal
npm run dev- start local development servernpm run build- build production outputnpm run preview- preview production build locallynpm run astro- run Astro CLI commands