evernote-debug-bundle
Debug Evernote API issues with diagnostic tools and techniques. Use when troubleshooting API calls, inspecting requests/responses, or diagnosing integration problems. Trigger with phrases like "debug evernote", "evernote diagnostic", "troubleshoot evernote", "evernote logs", "inspect evernote". 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 Debug Bundle
## Overview
Comprehensive debugging toolkit for Evernote API integrations, including request logging, ENML validation, token inspection, and diagnostic utilities.
## Prerequisites
- Evernote SDK installed
- Node.js environment
- Understanding of common Evernote errors
## Instructions
### Step 1: Debug Logger
```javascript
// utils/debug-logger.js
const fs = require('fs');
const path = require('path');
class EvernoteDebugLogger {
constructor(options = {}) {
this.enabled = options.enabled ?? process.env.EVERNOTE_DEBUG === 'true';
this.logFile = options.logFile || 'evernote-debug.log';
this.logDir = options.logDir || './logs';
this.maxLogSize = options.maxLogSize || 10 * 1024 * 1024; // 10MB
if (this.enabled) {
fs.mkdirSync(this.logDir, { recursive: true });
}
}
log(operation, data) {
if (!this.enabled) return;
const entry = {
timestamp: new Date().toISOString(),
operation,
...data
};
// Console output
console.log(`[EVERNOTE DEBUG] ${operation}`, JSON.stringify(data, null, 2));
// File output
this.writeToFile(entry);
}
logRequest(operation, params) {
this.log(operation, {
type: 'REQUEST',
params: this.sanitizeParams(params)
});
}
logResponse(operation, response) {
this.log(operation, {
type: 'RESPONSE',
response: this.summarizeResponse(response)
});
}
logError(operation, error) {
this.log(operation, {
type: 'ERROR',
errorCode: error.errorCode,
parameter: error.parameter,
identifier: error.identifier,
key: error.key,
rateLimitDuration: error.rateLimitDuration,
message: error.message,
stack: error.stack
});
}
sanitizeParams(params) {
// Remove sensitive data from logs
const sanitized = { ...params };
if (sanitized.token) sanitized.token = '[REDACTED]';
if (sanitized.password) sanitized.password = '[REDACTED]';
if (sanitized.content && sanitized.content.length > 500) {
sanitized.content = sanitized.content.substring(0, 500) + '...[truncated]';
}
return sanitized;
}
summarizeResponse(response) {
if (!response) return null;
// Summarize common response types
if (response.guid) {
return {
guid: response.guid,
title: response.title,
created: response.created,
updated: response.updated,
contentLength: response.content?.length
};
}
if (Array.isArray(response)) {
return {
count: response.length,
items: response.slice(0, 3).map(item => ({
guid: item.guid,
title: item.title || item.name
}))
};
}
return response;
}
writeToFile(entry) {
const filePath = path.join(this.logDir, this.logFile);
const line = JSON.stringify(entry) + '\n';
try {
fs.appendFileSync(filePath, line);
this.rotateIfNeeded(filePath);
} catch (error) {
console.error('Failed to write debug log:', error.message);
}
}
rotateIfNeeded(filePath) {
try {
const stats = fs.statSync(filePath);
if (stats.size > this.maxLogSize) {
const rotated = `${filePath}.${Date.now()}`;
fs.renameSync(filePath, rotated);
}
} catch (error) {
// Ignore rotation errors
}
}
}
module.exports = EvernoteDebugLogger;
```
### Step 2: Instrumented Client Wrapper
```javascript
// utils/instrumented-client.js
const Evernote = require('evernote');
const EvernoteDebugLogger = require('./debug-logger');
class InstrumentedEvernoteClient {
constructor(client, logger = null) {
this.client = client;
this.logger = logger || new EvernoteDebugLogger();
this._noteStore = null;
this._userStore = null;
}
get noteStore() {
if (!this._noteStore) {
this._noteStore = this.wrapStore(
this.client.getNoteStore(),
'NoteStore'
);
}
return this._noteStore;
}
get userStore() {
if (!this._userStore) {
this._userStore = this.wrapStore(
this.client.getUserStore(),
'UserStore'
);
}
return this._userStore;
}
wrapStore(store, storeName) {
const logger = this.logger;
return new Proxy(store, {
get(target, prop) {
const original = target[prop];
if (typeof original !== 'function') {
return original;
}
return async (...args) => {
const operation = `${storeName}.${prop}`;
const startTime = Date.now();
logger.logRequest(operation, {
args: args.map((arg, i) => ({
index: i,
type: typeof arg,
value: typeof arg === 'object' ? arg : String(arg).substring(0, 100)
}))
});
try {
const result = await original.apply(target, args);
const duration = Date.now() - startTime;
logger.logResponse(operation, {
duration,
result
});
return result;
} catch (error) {
const duration = Date.now() - startTime;
logger.logError(operation, {
duration,
error
});
throw error;
}
};
}
});
}
}
module.exports = InstrumentedEvernoteClient;
```
### Step 3: ENML Validator
```javascript
// utils/enml-validator.js
class ENMLValidator {
static validate(content) {
const errors = [];
const warnings = [];
// Required structure
if (!content.includes(''
});
}
if (!content.includes(''
});
}
if (!/]*>/.test(content)) {
errors.push({
type: 'MISSING_ROOT',
message: 'Missing root element',
fix: 'Wrap content in ... '
});
}
if (!content.includes(' ')) {
errors.push({
type: 'UNCLOSED_ROOT',
message: 'Missing closing tag',
fix: 'Add closing tag'
});
}
// Forbidden elements
const forbidden = [
{ pattern: /