๐Ÿ“… let's chat! explore the endless possibilities creating industries that don't exist. click here

evernote-local-dev-loop

Set up efficient local development workflow for Evernote integrations. Use when configuring dev environment, setting up sandbox testing, or optimizing development iteration speed. Trigger with phrases like "evernote dev setup", "evernote local development", "evernote sandbox", "test evernote locally". allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(node:*), Grep version: 1.0.0 license: MIT author: Jeremy Longshore <jeremy@intentsolutions.io>

Allowed Tools

No tools specified

Provided by Plugin

evernote-pack

Claude Code skill pack for Evernote (24 skills)

saas packs v1.0.0
View Plugin

Installation

This skill is included in the evernote-pack plugin:

/plugin install evernote-pack@claude-code-plugins-plus

Click to copy

Instructions

# Evernote Local Dev Loop ## Overview Configure an efficient local development environment for Evernote API integration with sandbox testing, hot reload, and debugging tools. ## Prerequisites - Completed `evernote-install-auth` setup - Node.js 18+ or Python 3.10+ - Evernote sandbox account at https://sandbox.evernote.com ## Instructions ### Step 1: Project Structure ```bash mkdir evernote-project && cd evernote-project npm init -y npm install evernote dotenv nodemon express express-session # Create project structure mkdir -p src/{routes,services,utils} touch src/index.js src/evernote-client.js .env .env.example ``` ``` evernote-project/ โ”œโ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ index.js # Express server entry โ”‚ โ”œโ”€โ”€ evernote-client.js # Evernote client wrapper โ”‚ โ”œโ”€โ”€ routes/ โ”‚ โ”‚ โ””โ”€โ”€ oauth.js # OAuth routes โ”‚ โ”œโ”€โ”€ services/ โ”‚ โ”‚ โ””โ”€โ”€ notes.js # Note operations โ”‚ โ””โ”€โ”€ utils/ โ”‚ โ””โ”€โ”€ enml.js # ENML helpers โ”œโ”€โ”€ .env # Local environment โ”œโ”€โ”€ .env.example # Template โ””โ”€โ”€ package.json ``` ### Step 2: Environment Configuration ```bash # .env EVERNOTE_CONSUMER_KEY=your-consumer-key EVERNOTE_CONSUMER_SECRET=your-consumer-secret EVERNOTE_SANDBOX=true EVERNOTE_DEV_TOKEN=your-sandbox-dev-token SESSION_SECRET=dev-session-secret-change-in-prod PORT=3000 NODE_ENV=development ``` ```bash # .env.example (commit this, not .env) EVERNOTE_CONSUMER_KEY= EVERNOTE_CONSUMER_SECRET= EVERNOTE_SANDBOX=true EVERNOTE_DEV_TOKEN= SESSION_SECRET= PORT=3000 NODE_ENV=development ``` ### Step 3: Evernote Client Wrapper ```javascript // src/evernote-client.js const Evernote = require('evernote'); class EvernoteService { constructor(accessToken) { this.client = new Evernote.Client({ token: accessToken, sandbox: process.env.EVERNOTE_SANDBOX === 'true' }); this._noteStore = null; this._userStore = null; } // Lazy-load NoteStore get noteStore() { if (!this._noteStore) { this._noteStore = this.client.getNoteStore(); } return this._noteStore; } // Lazy-load UserStore get userStore() { if (!this._userStore) { this._userStore = this.client.getUserStore(); } return this._userStore; } // Create client for OAuth flow static createOAuthClient() { return new Evernote.Client({ consumerKey: process.env.EVERNOTE_CONSUMER_KEY, consumerSecret: process.env.EVERNOTE_CONSUMER_SECRET, sandbox: process.env.EVERNOTE_SANDBOX === 'true' }); } // Quick dev client using developer token static createDevClient() { if (!process.env.EVERNOTE_DEV_TOKEN) { throw new Error('EVERNOTE_DEV_TOKEN not set - get one from sandbox.evernote.com'); } return new EvernoteService(process.env.EVERNOTE_DEV_TOKEN); } } module.exports = EvernoteService; ``` ### Step 4: ENML Utility Helpers ```javascript // src/utils/enml.js const ENML_HEADER = ` `; /** * Wrap content in valid ENML structure */ function wrapInENML(content) { return `${ENML_HEADER} ${content} `; } /** * Convert plain text to ENML */ function textToENML(text) { const escaped = text .replace(/&/g, '&') .replace(//g, '>') .replace(/\n/g, '
'); return wrapInENML(`
${escaped}
`); } /** * Convert Markdown to basic ENML (simplified) */ function markdownToENML(markdown) { let html = markdown // Headers .replace(/^### (.+)$/gm, '

$1

') .replace(/^## (.+)$/gm, '

$1

') .replace(/^# (.+)$/gm, '

$1

') // Bold and italic .replace(/\*\*(.+?)\*\*/g, '$1') .replace(/\*(.+?)\*/g, '$1') // Links .replace(/\[(.+?)\]\((.+?)\)/g, '$1') // Lists (simplified) .replace(/^- (.+)$/gm, '
โ€ข $1
') // Line breaks .replace(/\n\n/g, '
') .replace(/\n/g, '
'); return wrapInENML(`
${html}
`); } /** * Create todo checkbox */ function createTodo(text, checked = false) { return ` ${text}
`; } /** * Validate ENML has required structure */ function validateENML(content) { const errors = []; if (!content.includes('') || !content.includes('')) { errors.push('Missing root element'); } // Check for forbidden elements const forbidden = [' { if (content.toLowerCase().includes(tag)) { errors.push(`Forbidden element: ${tag}`); } }); return { valid: errors.length === 0, errors }; } module.exports = { ENML_HEADER, wrapInENML, textToENML, markdownToENML, createTodo, validateENML }; ``` ### Step 5: Express Server with OAuth ```javascript // src/index.js require('dotenv').config(); const express = require('express'); const session = require('express-session'); const EvernoteService = require('./evernote-client'); const app = express(); app.use(session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, cookie: { secure: process.env.NODE_ENV === 'production' } })); app.use(express.json()); // OAuth initiation app.get('/auth/evernote', (req, res) => { const client = EvernoteService.createOAuthClient(); const callbackUrl = `http://localhost:${process.env.PORT}/auth/evernote/callback`; client.getRequestToken(callbackUrl, (error, oauthToken, oauthTokenSecret) => { if (error) { console.error('OAuth error:', error); return res.status(500).json({ error: 'Failed to get request token' }); } req.session.oauthToken = oauthToken; req.session.oauthTokenSecret = oauthTokenSecret; res.redirect(client.getAuthorizeUrl(oauthToken)); }); }); // OAuth callback app.get('/auth/evernote/callback', (req, res) => { const client = EvernoteService.createOAuthClient(); client.getAccessToken( req.session.oauthToken, req.session.oauthTokenSecret, req.query.oauth_verifier, (error, accessToken, accessTokenSecret, results) => { if (error) { console.error('Access token error:', error); return res.status(500).json({ error: 'Failed to get access token' }); } req.session.accessToken = accessToken; req.session.evernoteExpires = results.edam_expires; res.json({ success: true, message: 'Authenticated successfully', expiresAt: new Date(parseInt(results.edam_expires)) }); } ); }); // Dev endpoint - uses dev token app.get('/dev/test', async (req, res) => { try { const evernote = EvernoteService.createDevClient(); const user = await evernote.userStore.getUser(); const notebooks = await evernote.noteStore.listNotebooks(); res.json({ user: user.username, notebookCount: notebooks.length, notebooks: notebooks.map(n => ({ name: n.name, guid: n.guid })) }); } catch (error) { res.status(500).json({ error: error.message }); } }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); console.log('Sandbox mode:', process.env.EVERNOTE_SANDBOX === 'true'); console.log('Dev endpoints:'); console.log(` GET /dev/test - Test with dev token`); console.log(` GET /auth/evernote - Start OAuth flow`); }); ``` ### Step 6: Package.json Scripts ```json { "scripts": { "start": "node src/index.js", "dev": "nodemon src/index.js", "test": "node --test", "test:note": "node -e \"require('./src/test-note.js')\"", "lint": "eslint src/" } } ``` ### Step 7: Quick Test Script ```javascript // src/test-note.js require('dotenv').config(); const EvernoteService = require('./evernote-client'); const { textToENML } = require('./utils/enml'); async function testCreateNote() { const evernote = EvernoteService.createDevClient(); // Create a test note const note = new (require('evernote')).Types.Note(); note.title = `Dev Test - ${new Date().toISOString()}`; note.content = textToENML('This is a test note from the dev loop'); const created = await evernote.noteStore.createNote(note); console.log('Created note:', created.guid); // Clean up - delete test note await evernote.noteStore.deleteNote(created.guid); console.log('Deleted test note'); console.log('Dev loop test passed!'); } testCreateNote().catch(console.error); ``` ## Development Workflow ```bash # Terminal 1: Run dev server with hot reload npm run dev # Terminal 2: Test API endpoints curl http://localhost:3000/dev/test # Quick note creation test npm run test:note ``` ## Sandbox vs Production | Aspect | Sandbox | Production | |--------|---------|------------| | URL | sandbox.evernote.com | www.evernote.com | | Dev Token | Available | Not available | | Rate Limits | Same | Same | | Data | Separate account | Real user data | | OAuth | Required for apps | Required | ## Output - Working local development server - Hot-reload enabled with nodemon - OAuth flow implemented - Dev token shortcut for quick testing - ENML utility functions ## Error Handling | Error | Cause | Solution | |-------|-------|----------| | `EVERNOTE_DEV_TOKEN not set` | Missing dev token | Get from sandbox.evernote.com/api/DeveloperToken.action | | `Invalid consumer key` | Wrong sandbox vs prod | Verify EVERNOTE_SANDBOX matches your key type | | `Session undefined` | Missing express-session | Ensure session middleware is configured | ## Resources - [Sandbox Environment](https://sandbox.evernote.com) - [Developer Tokens](https://dev.evernote.com/doc/articles/dev_tokens.php) - [OAuth Guide](https://dev.evernote.com/doc/articles/authentication.php) ## Next Steps Proceed to `evernote-sdk-patterns` for advanced SDK usage patterns.

Skill file: plugins/saas-packs/evernote-pack/skills/evernote-local-dev-loop/SKILL.md