Always define string constants in objects, never use strings directly in code.
// Define constants
export const MODE_NAMES = {
CONTENT: 'CONTENT',
FILES: 'FILES'
}
// Use constants
if (currentMode === MODE_NAMES.CONTENT) { ... }
const mode = MODE_NAMES.FILES// Don't use magic strings
if (currentMode === 'CONTENT') { ... } // ❌ No!
const mode = 'FILES' // ❌ No!- Typos caught at import time
- Easy refactoring (rename in one place)
- Autocomplete in IDE
- Clear what values are valid
Organize code by feature/command, not by technology layer.
src/kn/
├── search-command.mjs ← All search functionality together
├── create-command.mjs ← All create functionality together
src/
├── fzf.mjs ← Split by technology
├── ripgrep.mjs
├── modes.mjs
kn and keepnote commands must be completely independent.
// Both can import from shared utilities
import { getNotesPath } from '../config.mjs' // ✓ OK// Don't import between command directories
import { something } from '../keepnote/sync-command.mjs' // ❌ No!When a file grows beyond ~100 lines, use comment dividers.
// ============================================================================
// Mode Configuration
// ============================================================================
export const MODE_NAMES = { ... }
// ============================================================================
// Ripgrep Commands
// ============================================================================
export function fileContentSearchCommand() { ... }Don't create abstractions prematurely. Extract to separate file only when:
- File exceeds ~300 lines AND
- Clear logical separation exists AND
- Code is reused in multiple places
Keep related code together until there's a good reason to split.