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)
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(`
'); 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(` ${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 = ['
'); return wrapInENML(`
${html}
`);
}
/**
* Create todo checkbox
*/
function createTodo(text, checked = false) {
return ``; } /** * Validate ENML has required structure */ function validateENML(content) { const errors = []; if (!content.includes('') || !content.includes('')) { errors.push('Missing