# AGENTS.md - MD Reader Markdown reader application. ## Build & Run Commands ### Rust (Recommended) ```bash # Build project cargo build cargo build --release # Run application cargo run cargo run -- --help # Single binary output cargo build --release ./target/release/md_reader ``` ### Node.js / Electron (Alternative) ```bash # Install dependencies npm install # Development npm run dev # Production build npm run build # Run npm start ``` ## Testing ### Rust ```bash # Run all tests cargo test # Run single test by name cargo test test_name # Run with output cargo test -- --nocapture # Doc tests cargo test --doc ``` ### Node.js ```bash # Run all tests npm test # Run single test file npm test -- tests/single.test.ts # Run tests in watch mode npm run test:watch # Coverage npm run test:coverage ``` ## Linting & Formatting ### Rust ```bash # Format code (max_width=100, tab_spaces=4) cargo fmt cargo fmt -- --check # Lint cargo clippy cargo clippy --all-features # Check for errors cargo check ``` ### Node.js ```bash # Lint npm run lint # Format npm run format # Type check npm run typecheck ``` ## Code Style ### General - Keep lines under 100 characters - Use meaningful variable/function names - Document public APIs with doc comments ### Rust Style **Imports (order: std → external → local)** ```rust use std::path::Path; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use crate::core::parser::MarkdownParser; ``` **Error Handling** - Use `anyhow::Result` for application code - Use `thiserror` for library code - Use `.context()` for error context ```rust fn example() -> Result { let content = std::fs::read_to_string(path) .context("Failed to read markdown file")?; Ok(result) } ``` **Naming Conventions** - Types/Enums: PascalCase (`MarkdownDoc`, `NodeType`) - Functions/Variables: snake_case (`parse_markdown`, `render_html`) - Files: snake_case (`markdown_parser.rs`) - Traits: PascalCase (`Renderer`, `Parser`) **Types** ```rust #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MarkdownDoc { pub content: String, pub title: Option, } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "snake_case")] pub enum NodeType { Heading, Paragraph, CodeBlock, } ``` ### Node.js Style **Imports** ```typescript import { readFile } from 'fs/promises'; import { parse, Document } from 'markdown-it'; import type { Plugin } from './types'; ``` **Naming Conventions** - Types/Interfaces: PascalCase (`MarkdownDoc`, `RenderOptions`) - Functions/Variables: camelCase (`parseMarkdown`, `renderHtml`) - Files: kebab-case (`markdown-parser.ts`, `render-options.ts`) **Error Handling** ```typescript async function readMarkdown(path: string): Promise { try { return await readFile(path, 'utf-8'); } catch (error) { throw new Error(`Failed to read markdown: ${path}`, { cause: error }); } } ``` ## Project Structure ``` src/ ├── main.rs # Entry point (Rust) ├── lib.rs # Library exports (Rust) ├── parser/ # Markdown parsing logic ├── renderer/ # Output rendering (HTML, PDF, etc.) ├── ui/ # User interface components └── utils/ # Utility functions # Node.js alternative src/ ├── index.ts # Entry point ├── parser/ # Markdown parsing ├── renderer/ # Rendering logic ├── ui/ # UI components └── utils/ # Utilities ``` ## Key Dependencies (Rust) - **Error handling**: `anyhow`, `thiserror` - **Serialization**: `serde`, `serde_json` - **Markdown**: `pulldown-cmark` or `markdown` - **CLI**: `clap` (derive) - **Logging**: `tracing` ## Key Dependencies (Node.js) - **Markdown parsing**: `markdown-it`, `marked`, `remark` - **UI**: `React` or `Vue` - **Build**: `vite`, `webpack` - **Testing**: `vitest`, `jest` ## Cursor Rules (from momentry_core) - No unit tests exist - add tests when implementing features - Focus on clean, maintainable code - Use appropriate abstractions - Keep modules focused and single-purpose