# Photon Development Best Practices Guidelines for creating high-quality, consistent Photon MCPs. ## 1. JSDoc Header Tags Every Photon file must include these tags in the class-level JSDoc: ### Required Tags ```typescript /** * My Photon - Brief description * * Detailed description of what this photon does. * Include common use cases and examples. * * @version 1.0.0 * @author Portel * @license MIT */ ``` | Tag | Description | Example | |-----|-------------|---------| | `@version` | Semantic version | `@version 1.0.0` | | `@author` | Author name | `@author Portel` | | `@license` | License type | `@license MIT` | ### Conditional Tags | Tag | When to Use | Example | |-----|-------------|---------| | `@dependencies` | External npm packages | `@dependencies axios@^1.6.0, cheerio@^1.0.0` | | `@runtime` | Using newer runtime features (io, elicitation) | `@runtime ^1.4.0` | | `@tags` | Categorization for discovery | `@tags commerce, payments, api` | | `@stateful` | Maintains state across calls | `@stateful true` | | `@idleTimeout` | Custom idle timeout (ms) | `@idleTimeout 600000` | ### Example: Full Header ```typescript /** * Shopping Cart - AI-powered e-commerce assistant * * Demonstrates the AI+Human transaction workflow: * 1. AI suggests products based on query * 2. Human reviews and selects items * 3. Human confirms purchase * * Example: search({ query: "wireless headphones" }) * * @version 1.0.0 * @author Portel * @license MIT * @runtime ^1.4.0 * @dependencies axios@^1.7.0 * @tags commerce, ai-assistant, elicitation */ ``` --- ## 2. Method Naming Conventions Methods should read like natural English when called via CLI. ### Principles 1. **Avoid redundancy** - Don't repeat the class name in methods 2. **Use concise verbs** - Single words when possible 3. **Standard CRUD** - `create`, `get`, `list`, `update`, `delete` 4. **Singular vs plural** - Singular for one item, plural for collections ### Examples | Bad | Good | CLI Usage | |-----|------|-----------| | `getUsers()` | `users()` | `user-service.users` | | `getUserById()` | `user()` | `user-service.user` | | `createUser()` | `create()` | `user-service.create` | | `deleteUser()` | `delete()` | `user-service.delete` | | `getCurrentTime()` | `now()` | `time.now` | | `listTimezones()` | `timezones()` | `time.timezones` | | `addToCart()` | `add()` | `shop.add` | | `viewCart()` | `cart()` | `shop.cart` | | `applyDiscount()` | `discount()` | `shop.discount` | ### Standard Verbs | Action | Verb | Example | |--------|------|---------| | Get single item | `get` or noun | `user()`, `product()` | | List items | plural noun | `users()`, `products()` | | Create | `create`, `add`, `new` | `create()`, `add()` | | Update | `update`, `set` | `update()`, `set()` | | Delete | `delete`, `remove` | `delete()`, `remove()` | | Search | `search`, `find` | `search()`, `find()` | | Execute | `run`, `execute` | `run()`, `execute()` | --- ## 3. Method Documentation Every public method needs proper JSDoc. ### Required Elements ```typescript /** * Brief description of what the method does * * @param paramName Description of parameter * @format list {@title name, @subtitle description} */ async users(params?: { limit?: number }): Promise ``` ### Parameter Documentation ```typescript /** * Search products in catalog * * @param query Search query string * @param category Filter by category {@example "electronics"} * @param limit Max results (default: 10) {@min 1} {@max 100} */ async search(params: { query: string; category?: string; limit?: number; }): Promise ``` ### Format Hints | Format | Use Case | |--------|----------| | `@format list` | Array of objects | | `@format table` | Tabular data | | `@format card` | Single object | | `@format json` | Raw JSON | | `@format markdown` | Markdown content | | `@format html` | HTML content | | `@format none` | No rendering (actions) | ### UI Template Linking ```typescript /** * Dashboard main view * * @icon 📋 * @ui tasks */ async main(): Promise ``` --- ## 4. Code Organization ### File Structure ``` my-photon.photon.ts # Main photon file my-photon/ ui/ tasks.html # Custom UI templates overview.html data/ config.json # Persistent data ``` ### Class Structure ```typescript export default class MyPhoton { // 1. Private fields private data: Map; // 2. Constructor constructor(config?: string) { } // 3. Lifecycle hooks async onInitialize() { } // 4. Public methods (alphabetical or grouped by feature) async methodA() { } async methodB() { } // 5. Private helpers (prefixed with _) private _helper() { } } ``` ### Naming Conventions | Element | Convention | Example | |---------|------------|---------| | Class | PascalCase | `ShoppingCart` | | Methods | camelCase | `addItem()` | | Private methods | _prefix | `_validateInput()` | | Constants | UPPER_SNAKE | `MAX_ITEMS` | | Interfaces | PascalCase | `CartItem` | --- ## 5. Error Handling ### Throw Descriptive Errors ```typescript // Good if (!cart) { throw new Error(`Cart not found: ${cartId}`); } // Bad if (!cart) { throw new Error('Not found'); } ``` ### Validate Input Early ```typescript async update(params: { id: string; quantity: number }) { if (!params.id) { throw new Error('Product ID is required'); } if (params.quantity < 0) { throw new Error('Quantity must be non-negative'); } // ... proceed with valid input } ``` --- ## 6. Return Value Guidelines ### Consistent Response Structure ```typescript // For actions that might fail return { success: true, message: 'Item added to cart', data: { cartId, itemCount } }; // For queries return { items: [...], total: 42, page: 1 }; ``` ### Use Formatted Strings for Display ```typescript // Good - formatted for display return { price: `$${amount.toFixed(2)}`, date: new Date(timestamp).toLocaleDateString() }; // Bad - raw values return { price: 29.99, date: '2024-01-15T10:30:00Z' }; ``` --- ## 7. State Management ### Persist Important Data ```typescript const DATA_FILE = path.join(os.homedir(), '.photon', 'my-photon', 'data.json'); private async load(): Promise { try { const content = await fs.readFile(DATA_FILE, 'utf-8'); return JSON.parse(content); } catch { return DEFAULT_DATA; } } private async save(data: Data): Promise { await fs.mkdir(path.dirname(DATA_FILE), { recursive: true }); await fs.writeFile(DATA_FILE, JSON.stringify(data, null, 2)); } ``` ### Mark Stateful Photons ```typescript /** * @stateful true * @idleTimeout 600000 */ ``` --- ## 8. Testing Checklist Before release, verify: - [ ] `scripts/pre-release-check.sh` passes before tagging or publishing - [ ] `tests/daemon-chaos.test.ts` passes; daemon spawn races block release - [ ] `tests/contract/render-dom.test.ts` passes with Playwright Chromium installed - [ ] All methods have JSDoc with descriptions - [ ] All parameters are documented - [ ] Error cases throw descriptive errors - [ ] Return values are properly formatted - [ ] State persists correctly (if applicable) - [ ] No hardcoded paths (use `os.homedir()`) - [ ] Dependencies are declared in header - [ ] Version, author, license tags present --- ## 9. Quick Reference ### Minimal Photon Template ```typescript /** * My Photon - Brief description * * @version 1.0.0 * @author Portel * @license MIT */ export default class MyPhoton { /** * Main entry point */ async main(): Promise { return 'Hello from MyPhoton!'; } } ``` ### Full-Featured Template ```typescript /** * My Photon - Comprehensive description * * Detailed explanation of features: * - Feature 1 * - Feature 2 * * Example: main({ option: "value" }) * * @version 1.0.0 * @author Portel * @license MIT * @runtime ^1.4.0 * @dependencies axios@^1.7.0 * @tags category1, category2 */ import { io } from '@portel/photon-core'; interface Item { id: string; name: string; } export default class MyPhoton { private items: Item[] = []; async onInitialize() { // Setup logic } /** * List all items * @format list {@title name} */ async items(): Promise { return this.items; } /** * Add a new item * @param name Item name */ async add(params: { name: string }): Promise { const item = { id: String(Date.now()), name: params.name }; this.items.push(item); return item; } } ```