AGENTS.md - AI Coding Agent Guidelines
This is a Jekyll static site blog hosted on GitHub Pages at sameerbajaj.com. Primary Focus: SEO & organic search traffic.
Build Commands
# Development server (user runs this - agents should NOT start the server)
bundle exec jekyll serve --livereload --config _config.yml,_config-dev.yml
# Production build
bundle exec jekyll build
# Clean build cache
bundle exec jekyll clean
# Check configuration
bundle exec jekyll doctor
# HTML validation (after building _site/)
bundle exec rake
Testing & Validation
There are no unit tests. Validation is done via:
bundle exec rake # Runs HTMLProofer on _site/
bundle exec jekyll doctor # Configuration checks
SEO Submission Scripts (Node.js)
npm run submit:google # Submit URLs to Google Indexing API
npm run submit:bing # Submit URLs to Bing IndexNow
npm run submit:all # Bulk submission to both
npm run urls:list # List all site URLs
npm run check:status # Check indexing status
Requires GOOGLE_SERVICE_ACCOUNT_JSON environment variable for Google API.
Project Structure
_config.yml # Production config
_config-dev.yml # Development overrides (incremental builds, no minification)
_posts/ # Blog posts (YYYY-MM-DD-slug.md)
_layouts/ # HTML templates (post.html, book_notes.html, page.html)
_includes/ # Reusable components (head.html, nav.html, etc.)
_sass/ # SCSS partials (compiled via jekyll-minifier)
_data/ # YAML data files (fiction_books.yml, tools.yml)
assets/js/ # JavaScript (source .js and minified .min.js)
assets/images/ # Post images
scripts/ # Node.js SEO submission utilities
docs/ # Internal documentation
Code Style Guidelines
JavaScript Conventions
// Use DOMContentLoaded for DOM-dependent code
document.addEventListener('DOMContentLoaded', function () {
// Code here
});
// IIFE pattern for isolation
(function () {
// Self-contained logic
})();
// Early returns for guard clauses
if (!element || !container) {
return;
}
// Function declarations over expressions for named functions
function setDarkMode(isDarkMode) { }
function toggleDarkMode() { }
// Arrow functions for callbacks and event handlers
prefersDarkMode.addEventListener('change', (e) => {
setDarkMode(e.matches);
});
// LocalStorage for persistence
localStorage.getItem('darkMode');
localStorage.setItem('darkMode', isDarkMode);
Important: Production serves .min.js files. When editing JavaScript in assets/js/, update both source and minified versions.
SCSS/CSS Conventions
// Component-based naming (BEM-like)
.toc { }
.toc h2 { }
.toc a.active { }
// CSS custom properties for theming
color: var(--signature-color);
background: var(--background-color);
// Dark mode via body class
body.dark-mode .component { }
// Responsive breakpoints
@media (max-width: 768px) { }
@media (max-width: 1360px) { }
// Modern CSS features encouraged
clamp(), min(), max(), transform, transition
SCSS partials use underscore prefix: _toc.scss, _newsletter.scss.
Liquid Template Patterns
<!-- Simple Feedback Widget - One-click reactions -->
<div class="feedback-widget" data-post-title="AGENTS.md - AI Coding Agent Guidelines" data-post-url="/AGENTS/">
<div class="feedback-section" id="feedbackSection">
<p class="feedback-question">Was this helpful?</p>
<!-- Reaction Buttons -->
<div class="reaction-buttons">
<button class="reaction-btn" data-reaction="loved" title="Loved it!">
<span class="emoji">❤️</span>
<span class="reaction-label">Loved it</span>
</button>
<button class="reaction-btn" data-reaction="helpful" title="Helpful">
<span class="emoji">👍</span>
<span class="reaction-label">Helpful</span>
</button>
<button class="reaction-btn" data-reaction="interesting" title="Interesting">
<span class="emoji">🤔</span>
<span class="reaction-label">Interesting</span>
</button>
<button class="reaction-btn" data-reaction="meh" title="Meh">
<span class="emoji">😐</span>
<span class="reaction-label">Meh</span>
</button>
</div>
<!-- Thank you message (shown inline after click) -->
<p class="feedback-thanks" id="thanksMessage" style="display: none;">
Thanks! ✨
</p>
<!-- Optional comment section (shown after reaction click) -->
<div class="feedback-comment-section" id="commentSection" style="display: none;">
<p class="comment-prompt">Want to add a quick comment? (optional)</p>
<textarea
class="comment-input"
id="commentInput"
placeholder="What did you think?"
maxlength="500"
rows="3"
></textarea>
<div class="comment-buttons">
<button class="comment-btn comment-submit" id="submitComment">Send</button>
<button class="comment-btn comment-skip" id="skipComment">Skip</button>
</div>
</div>
</div>
</div>
<style>
.feedback-widget {
margin: 2.4rem 0 1.6rem 0;
padding: 2rem 1.6rem;
border-top: 1px solid var(--border-color);
border-bottom: 1px solid var(--border-color);
text-align: center;
background: var(--background-color);
}
.feedback-question {
margin: 0 0 1.6rem 0;
font-size: 1.8rem;
font-family: 'signifier', serif;
color: var(--heading-color);
font-weight: 400;
line-height: 1.4;
}
.reaction-buttons {
display: flex;
justify-content: center;
gap: 1.2rem;
flex-wrap: wrap;
margin-bottom: 0.8rem;
}
.reaction-btn {
background: var(--background-elevated);
border: 1.5px solid var(--border-color);
border-radius: 12px;
width: 72px;
height: 72px;
cursor: pointer;
transition: all 0.25s ease;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.4rem;
position: relative;
box-shadow: 0 1px 3px rgba(0,0,0,0.08);
}
.reaction-btn:hover {
transform: translateY(-3px);
border-color: var(--signature-color);
box-shadow: 0 3px 10px rgba(0,0,0,0.12);
}
.reaction-btn:hover .reaction-label {
opacity: 1;
color: var(--signature-color);
}
.reaction-btn.clicked {
transform: scale(0.95);
background: var(--background-hover);
border-color: var(--signature-color);
}
.reaction-btn .emoji {
font-size: 2.2rem;
display: block;
line-height: 1;
}
.reaction-label {
font-size: 1.1rem;
font-family: 'signifier', serif;
color: var(--text-muted);
font-weight: 400;
opacity: 0.85;
transition: all 0.25s ease;
text-transform: capitalize;
}
.feedback-thanks {
margin-top: 1.2rem;
color: var(--success-color);
font-size: 1.6rem;
font-family: 'signifier', serif;
font-weight: 400;
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-8px); }
to { opacity: 1; transform: translateY(0); }
}
.feedback-comment-section {
margin-top: 1.6rem;
animation: fadeIn 0.5s ease;
}
.comment-prompt {
margin: 0 0 1.2rem 0;
font-size: 1.5rem;
font-family: 'signifier', serif;
color: var(--text-muted);
font-weight: 400;
}
.comment-input {
width: 100%;
max-width: 480px;
padding: 1rem;
border: 1.5px solid var(--border-color);
border-radius: 10px;
font-family: 'signifier', serif;
font-size: 1.4rem;
line-height: 1.6;
resize: vertical;
transition: all 0.25s ease;
background: var(--background-color);
color: var(--text-color);
}
.comment-input:focus {
outline: none;
border-color: var(--signature-color);
box-shadow: 0 0 0 2.5px rgba(0,0,0,0.05);
}
.comment-input::placeholder {
color: var(--text-muted-light);
opacity: 0.8;
}
.comment-buttons {
display: flex;
gap: 0.8rem;
justify-content: center;
margin-top: 1.2rem;
}
.comment-btn {
padding: 0.8rem 2rem;
border: none;
border-radius: 6px;
font-size: 1.4rem;
font-family: 'signifier', serif;
font-weight: 400;
cursor: pointer;
transition: all 0.25s ease;
text-decoration: none;
}
.comment-submit {
background: var(--signature-color);
color: white;
}
.comment-submit:hover {
background: var(--signature-color-hover);
transform: translateY(-1.5px);
box-shadow: 0 3px 10px rgba(245, 158, 11, 0.3);
}
.comment-skip {
background: transparent;
color: var(--text-muted);
border: 1.5px solid var(--border-color);
}
.comment-skip:hover {
background: var(--background-hover);
border-color: var(--border-color-hover);
}
@media only screen and (max-width: 780px) {
.feedback-widget {
padding: 1.6rem 0.8rem;
}
.feedback-question {
font-size: 1.45rem;
}
.reaction-buttons {
gap: 0.8rem;
}
.reaction-btn {
width: 60px;
height: 60px;
}
.reaction-btn .emoji {
font-size: 1.9rem;
}
.reaction-label {
font-size: 0.95rem;
}
.comment-prompt {
font-size: 1.3rem;
}
.comment-input {
font-size: 1.2rem;
}
.comment-btn {
padding: 0.7rem 1.6rem;
font-size: 1.2rem;
}
}
</style>
/assets/js/file.js?v=1793392977
Markdown Conventions
- Use consistent header hierarchy (H1 for title, H2 for sections)
- Quote YAML strings with special chars:
title: "Book: The Title" - Use ISO 8601 dates:
YYYY-MM-DD HH:MM - Consistent tags:
book-notes,blog, etc.
Front Matter Standards
Blog Post
---
title: "Post Title"
layout: post
date: 2024-03-08 00:00
image: /assets/images/image.jpg
description: "Brief summary (150 chars)"
tags: [tag1, tag2]
category: blog
author: sameerbajaj
last_modified_at: 2024-01-02 12:00 # Update when revising content
---
Book Notes
---
title: "Book Title by Author - Summary and Notes"
layout: book_notes
date: 2024-03-08 00:00
image_url: https://m.media-amazon.com/...
book_title: "Book Title"
book_author: "Author Name"
book_url: https://amzn.to/...
book_thoughts: "One-liner recommendation"
rating: 7
date_read: 2024-01-27 00:00
star: false
tags: [book-notes]
category: blog
---
File Naming
- Posts:
YYYY-MM-DD-slug-title.md(slug becomes URL path) - JavaScript: maintain both
.jsand.min.jsversions - SCSS partials: prefix with
_ - Images: descriptive names, prefer JPEG for photos
SEO Best Practices (Critical)
- Titles: Front-load keywords, keep under 60 chars
- Descriptions: 150-160 chars, include keywords naturally
- Headings: Proper hierarchy (H1 -> H2 -> H3), include semantic keywords
- URLs: Keep slugs descriptive and concise
- Internal linking: Link to related posts when relevant
- Images: Always include descriptive
alttext - Content freshness: Update
last_modified_atwhen revising - OG images: 1200x630px, under 300KB, JPEG preferred
OG Image Fallback Logic
- Use
image(relative path) - Fallback to
image_url(absolute URL) - Auto-extract first
<img>from content - Last resort: site avatar
Development Workflow
IMPORTANT: The user runs the Jekyll dev server themselves. Never start the server or run bundle exec jekyll serve. If you need the user to check changes, ask them to review it.
- Edit files as needed
- Ask user to verify changes in browser (they’re running dev server)
- For significant revisions, update
last_modified_at - After commit/push, consider
npm run submit:googlefor faster indexing
Ruby Environment
- Uses rbenv for Ruby version management
- Ruby 3.2.2 specified
- Always use rbenv’s Ruby, not system Ruby
- Run
bundle installafter Gemfile changes
Error Handling
- JavaScript: Use early returns and guard clauses
- Check element existence before DOM operations
- Use
try/catchfor async operations in Node.js scripts
Performance Patterns
- CSS inlined in
<head>via SCSS compilation - Non-critical CSS:
media="print" onload="this.media='all'" - JavaScript with
deferattribute - Font preloading for critical fonts
jekyll-minifierfor production HTML/CSS/JS compression
Author & Submissions Identity Standards
- Twitter / X Handle:
sameerbajajstrictly (never include an underscoresameerbajaj_). - LinkedIn:
sameerbajaj(https://www.linkedin.com/in/sameerbajaj). - Minimal Information Principle: In all web directory, registry, or tool submissions, only populate strictly mandatory/required fields. Do not fill out unnecessary personal profiles, phone numbers, or optional social accounts.
- Employer Privacy Invariant: NEVER disclose or mention current employer / company name in public profiles, author bios, directories, or submissions. Always identify generically as “Product Manager” or “Software Builder” strictly.
Backlink & Promotion Standards (4-Gate Anti-Spam Filter)
To prevent spammy, low-taste, or mismatched outreach, every prospective link target must pass all 4 gates:
- Exact Form Factor Match:
- macOS Desktop Apps (
MacYT,EnhancedSkills) ➔ ONLY macOS app showcases (awesome-mac,open-source-mac-os-apps). NEVER submit desktop apps to CLI lists or generic software directories. - Essays & Case Studies ➔ ONLY technical/product publications (
Dev.towith canonical tags,HackerNoon,Mind the Product). - Personal Blog & Reading Notes ➔ ONLY authentic indie webrings (
uses.tech,personalsit.es,blogroll.org).
- macOS Desktop Apps (
- Objective Prerequisite Verification:
- Pre-audit star counts, repository age, and automated maintainer bots before proposing. Never propose a list requiring >4 stars if the repo currently has fewer.
- Immediately disqualify any directory requiring revenue, MRR, or payment processor sync (e.g. TrustMRR). All tools are 100% free open-source software.
- Taste & Reputation Invariant (“Proud to Showcase” Test):
- Zero SEO link farms, low-rent directories, throwaway profile aggregators, or paid listing networks (e.g. Carrd hubs, Linktree spam, Dodo, TinyLaunch).
- If the author would not proudly display the listing on their portfolio, do not pursue it.
- Editorial & Peer Recognition Over Directory Volume:
- Prioritize high-signal, earned editorial mentions (canonical syndication on Dev.to, guest technical breakdowns, mutual
/friendsblogroll links) over arbitrary directory submissions.
- Prioritize high-signal, earned editorial mentions (canonical syndication on Dev.to, guest technical breakdowns, mutual
Technical SEO Architecture & Crawl Resolutions (September 2026 Audit)
- Cloudflare Edge Redirects (
_redirects):- Always maintain
_redirectsin root and register in_config.ymlinclude:array. - Enforce explicit
301status codes for slug aliases, case normalization (/psychologyofmoney/->/PsychologyOfMoney/), and trailing slashes.
- Always maintain
- Email Obfuscation Bot Guard (
<!--email_off-->):- Cloudflare Scrape Shield dynamically rewrites raw
mailto:and email addresses into/cdn-cgi/l/email-protectionendpoints, which return 404 to search bots. - Always wrap email addresses and mailto links in
<!--email_off-->...<!--/email_off-->to prevent crawl errors.
- Cloudflare Scrape Shield dynamically rewrites raw
- Structured Data & Schema.org:
- Emits dynamic
BreadcrumbListJSON-LD across all pages and enrichedPersonKnowledge Graph schema on the homepage. - Never allow date inversions (
last_modified_at < date).
- Emits dynamic
- Template Hygiene:
_layouts/post.html: Never open a nested<body>tag inside layouts (it wraps inside_layouts/default.html). Body classes should be passed viapage.body_classor inherited cleanly.
- Mobile Touch Targets:
- Interactive filter buttons (
.tag-filter__button) must havemin-height: 44px; min-width: 44px; display: inline-flex; align-items: center; justify-content: center;to pass Google Mobile & Apple HIG guidelines.
- Interactive filter buttons (
- AI Crawler Readiness (
llms.txt):- Slugs in
llms.txtmust match Jekyll permalinks exactly.
- Slugs in