Blog Development Guidelines for Claude
IMPORTANT: Read First
Before starting any work, you need to clone the chop-conventions repository (this is used during development in containers):
mkdir -p repo_tmp
cd repo_tmp
git clone https://github.com/idvorkin/chop-conventions.git
Then read and follow the instructions in:
repo_tmp/chop-conventions/dev-inner-loop/a_readme_first.md
This file will direct you to all other convention files you need to follow.
Build/Test/Lint Commands
- Build TypeScript:
just js-build
- Watch for changes:
just js-watch
- Run unit tests (fast):
just fast-test
- Run E2E tests:
just e2e-test
- Run specific test:
npx vitest run src/__tests__/main.test.ts
- Run tests in watch mode:
just js-test-watch
- Check code formatting:
just js-lint
- Format code:
just js-format
- Validate code (typecheck + lint):
just js-validate
- Serve local Jekyll site:
just jekyll-serve
(usually already running at http://localhost:4000) - Update backlinks:
just update-backlinks
Blog Writing Style
VERY IMPORTANT: Always reference and follow the comprehensive content guidelines in content_guidelines.md
when working on any blog posts. This file contains:
- 8 distinct content types with specific guidelines
- Universal writing style rules
- Technical implementation details for alert boxes, redirects, book links, and images
- Content development workflow
Never skip consulting content_guidelines.md when creating or editing blog content.
Finding Related Blog Content
When you need to find existing blog posts on a specific topic or merge new content into existing posts:
- Use the blog-topic-finder agent (via Task tool with subagent_type: blog-topic-finder)
- This agent searches through back-links.json and markdown files to identify relevant posts
- Especially useful when:
- User wants to add insights or content to existing posts
- Finding all posts related to a specific theme
- Checking what’s already been written before creating new content
- Merging journal entries or thoughts into established posts
Example: When user says “I want to work on a post about X” or “merge this content into…”, always use the blog-topic-finder agent first.
Important: The blog-topic-finder agent should always start by pulling ALL titles and descriptions from back-links.json to have complete context for searching:
jq '.url_info | to_entries[] | {url: .key, title: .value.title, description: .value.description}' back-links.json
This ensures comprehensive searching across all blog content.
Working with back-links.json
The back-links.json
file (~283KB, ~5900 lines) contains metadata about all blog posts. Use jq
for efficient querying instead of loading the entire file.
Structure:
{
"redirects": { "/old-url": "/new-url", ... },
"url_info": {
"/post-url": {
"title": "Post Title",
"description": "Post description...",
"doc_size": 16000,
"file_path": "_site/post.html",
"markdown_path": "_d/post.md",
"last_modified": "2024-05-05T08:25:00-07:00",
"incoming_links": ["/linking-post1", "/linking-post2"],
"outgoing_links": ["/linked-post1", "/linked-post2"],
"redirect_url": "",
"url": "/post-url"
},
...
}
}
Efficient jq queries:
# Get all post URLs and titles
jq '.url_info | to_entries[] | {url: .key, title: .value.title}' back-links.json
# Search for posts by title (case-insensitive)
jq '.url_info | to_entries[] | select(.value.title | ascii_downcase | contains("family")) | {url: .key, title: .value.title}' back-links.json
# Find posts with specific incoming links
jq '.url_info | to_entries[] | select(.value.incoming_links[]? == "/eulogy") | .key' back-links.json
# Get post metadata by URL
jq '.url_info["/gap-year-igor"]' back-links.json
# Search descriptions for keywords
jq '.url_info | to_entries[] | select(.value.description | ascii_downcase | contains("priority")) | {url: .key, title: .value.title}' back-links.json
Code Style Guidelines
- Use TypeScript for all new code with strict typing
- Follow existing patterns in the codebase
- Export functions deliberately, keep helpers private
- Always explicitly import all functions needed from other modules
- Check document.readyState before DOM manipulation
- Prefix console.logs with emojis for easier debugging
- Use interfaces for data structures in shared.ts
- Handle errors gracefully with fallback content
- Respect jQuery integration patterns
- Use consistent HTML element IDs
- Append
| /bin/cat
to terminal commands using pagers - Test components in isolation
- Update backlinks.json after content changes
- Minimize asset sizes and optimize performance
Test Structure
- Unit tests (Vitest): Located in
src/__tests__/
- test individual functions and components - E2E tests (Playwright): Located in
tests/e2e/
- test full page interactions and user flows
Important: Always Add Both Test Types
When adding new features or fixing bugs, always add both unit tests and e2e tests:
- Unit tests verify the logic works correctly in isolation
- E2E tests verify the feature works in the real browser environment
- For features that use environment variables (like SERVER_PORT), ensure tests handle them properly
Development & Server
Development Workflow
- Run
just js-build
after TypeScript changes - Create isolated test cases for complex components
- Use the blog’s existing structure for new features
- Check Jekyll at http://localhost:4000 when developing
- When running e2e tests, always use headless mode (the default) and NOT
--debug
mode - If NPM tools are not installed, install them
Running Jekyll Server
- Default:
just jekyll-serve
(port 4000, livereload port 35729) - Custom port:
just jekyll-serve 4002
- Custom ports with livereload:
just jekyll-serve 4002 35730
- Run in background:
just jekyll-serve 4002 35730 > /tmp/jekyll.log 2>&1 &
- Check port availability first: If port in use, pick random port between 5000-6000
- Use random live reload port: Pick a random live reload port (e.g., 35000-36000) to avoid conflicts
- Open page in browser:
open http://localhost:4002/page-name
- Open specific section:
open http://localhost:4002/page-name#section-anchor
Git & PR Workflow
Basic Workflow Rules
- Always work on a PR, never on main directly
- Use main branch (not master)
- Start with fetch/rebase to get latest main
- All PRs should target main branch
- Before starting work, make sure you have the latest main branch
Commit Guidelines
- Generated JS files ARE committed (assets/js/index.js, assets/js/index.js.map)
- Commit both TypeScript source files (src/*.ts) and the generated JS bundle
- JS files are built from TypeScript and should be tracked in git for Jekyll deployment
- Always run
just js-build
before committing to ensure JS bundle is up to date - Break commits into logical changes
- Run pre-commit on files before editing (keeps reformats separate from changes)
- Never bypass commit hooks
- Put Claude/agent config changes in separate commits
- Always commit after making changes to claude config (agents or claude config)
PR Best Practices
- Create new PR for each task
- When starting work on an issue, update the issue with the linked PR
- Run code review agent before pushing
- When updating a PR with significant changes, update the PR description to reflect all current changes
- Include any fixes or improvements made during development in the PR description
- After updating PR, open in the web browser
- Prompt the user asking if they want you to open the PR for them
- On startup, ask user if they want to continue a PR, list the PRs in the subdirectories (named after their branches)
- Never merge PRs yourself - the user must do this from the PR
Post-Merge Cleanup
After a PR is merged, always:
- First verify the PR is actually merged:
gh pr view <pr-number> --json state,merged
- Switch to main:
git checkout main
- Pull latest changes:
git pull
- Delete local branch:
git branch -d <branch-name>
- Delete remote branch:
git push origin --delete <branch-name>
This keeps the repository clean and ensures you’re always working with the latest code.
PR Feedback Resolution
When resolving PR feedback:
- Always commit and push changes after implementing feedback
- Check for uncommitted changes before starting or finishing work
- If uncommitted changes exist, ask the user before discarding them
- The pr-feedback-resolver agent should always push changes to remote
Temporary Files
- Use
/home/developer/gits/idvorkin.github.io/repo_tmp/
for all temporary files instead of/tmp/
- This directory is gitignored and stays within the repo for easier access
- Always use full paths when creating temp files:
/home/developer/gits/idvorkin.github.io/repo_tmp/filename.ext
- Examples:
- Screenshots:
/home/developer/gits/idvorkin.github.io/repo_tmp/screenshot.png
- Test output:
/home/developer/gits/idvorkin.github.io/repo_tmp/test-output.txt
- Jekyll logs:
/home/developer/gits/idvorkin.github.io/repo_tmp/jekyll.log
- Screenshots:
Clipboard Access
- Use
osascript
instead ofpbpaste
for checking clipboard content as it can handle multiple content types (images, HTML, text) - First check what’s on clipboard:
osascript -e 'clipboard info'
- For text content:
osascript -e 'the clipboard as text'
- For image content: Use the
image-content-analyzer
agent (Task tool) which handles clipboard images efficiently- The agent will extract, convert to WebP, and analyze the image
- Example: “User has copied an image to clipboard and wants it analyzed”
- Only extract images manually if the image-content-analyzer agent is not available
- Ensure you never approve a PR (maintain proper review processes and security)
- when fixing an issue with a PR, be sure to put a comment on the issue that you’er working on it via PR