chore: initialize EverOS 1.0.0
md-first memory extraction framework for AI agents. Markdown is the single source of truth; SQLite holds state and LanceDB provides the rebuildable vector + BM25 + scalar index. The codebase follows a single-direction DDD layering (entrypoints -> service -> memory -> infra, with component / core / config cross-cutting) enforced by import-linter. Engineering surface: - Coding conventions in .claude/rules/ (path-scoped) and workflows in .claude/skills/ (/commit, /new-branch, /pr). - GitHub Actions CI runs make lint + test + integration; pre-commit mirrors the gates locally (ruff, hygiene hooks, gitlint commit-msg). - Commit messages follow Conventional Commits, enforced by gitlint. - make lint also enforces datetime two-zone discipline and OpenAPI drift.
This commit is contained in:
96
use-cases/claude-code-plugin/scripts/test-retrieve-memories.js
Executable file
96
use-cases/claude-code-plugin/scripts/test-retrieve-memories.js
Executable file
@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Test script for retrieving memories from EverMem Cloud
|
||||
* Simulates what happens when user submits a prompt
|
||||
*
|
||||
* Usage:
|
||||
* export EVERMEM_API_KEY="your-key"
|
||||
* node scripts/test-retrieve-memories.js
|
||||
*/
|
||||
|
||||
import { getConfig, isConfigured } from '../hooks/scripts/utils/config.js';
|
||||
import { searchMemories, transformSearchResults } from '../hooks/scripts/utils/evermem-api.js';
|
||||
import { formatRelativeTime } from '../hooks/scripts/utils/mock-store.js';
|
||||
|
||||
// Test queries that should match saved memories
|
||||
const TEST_QUERIES = [
|
||||
"How do we handle authentication?",
|
||||
"What's our database setup?",
|
||||
"Tell me about rate limiting",
|
||||
"How are errors handled in the API?",
|
||||
"What was that N+1 query issue?",
|
||||
"JWT token configuration",
|
||||
"PostgreSQL connection pooling"
|
||||
];
|
||||
|
||||
async function main() {
|
||||
console.log('🔍 EverMem Retrieve Memory Test\n');
|
||||
console.log('=' .repeat(60));
|
||||
|
||||
// Check configuration
|
||||
if (!isConfigured()) {
|
||||
console.error('❌ EVERMEM_API_KEY not set');
|
||||
console.error(' Run: export EVERMEM_API_KEY="your-key"');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = getConfig();
|
||||
console.log(`✓ API Key: ${config.apiKey.slice(0, 10)}...`);
|
||||
console.log(`✓ User ID: ${config.userId}`);
|
||||
console.log(`✓ Group ID: ${config.groupId}`);
|
||||
console.log(`✓ API URL: ${config.apiBaseUrl}`);
|
||||
console.log('=' .repeat(60));
|
||||
|
||||
for (const query of TEST_QUERIES) {
|
||||
console.log(`\n📝 Query: "${query}"`);
|
||||
console.log('-'.repeat(60));
|
||||
|
||||
try {
|
||||
// Call API exactly like inject-memories.js does
|
||||
const apiResponse = await searchMemories(query, {
|
||||
topK: 5,
|
||||
retrieveMethod: 'hybrid'
|
||||
});
|
||||
|
||||
// Transform response exactly like inject-memories.js does
|
||||
const memories = transformSearchResults(apiResponse);
|
||||
|
||||
if (memories.length === 0) {
|
||||
console.log(' No memories found');
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(` Found ${memories.length} memories:\n`);
|
||||
|
||||
// Display like the plugin does
|
||||
for (let i = 0; i < Math.min(memories.length, 3); i++) {
|
||||
const memory = memories[i];
|
||||
const relTime = formatRelativeTime(memory.timestamp);
|
||||
const shortText = memory.text.length > 70
|
||||
? memory.text.slice(0, 70) + '...'
|
||||
: memory.text;
|
||||
|
||||
console.log(` ${i + 1}. (${relTime}) [${memory.type}]`);
|
||||
console.log(` "${shortText}"`);
|
||||
if (memory.score) {
|
||||
console.log(` Score: ${memory.score.toFixed(3)}`);
|
||||
}
|
||||
console.log('');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(` ❌ Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n' + '=' .repeat(60));
|
||||
console.log('\n✅ Retrieval test complete!');
|
||||
console.log('\nTo test in Claude Code:');
|
||||
console.log(' claude --plugin-dir /Users/hzh/code/memory-plugin');
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
console.error('Fatal error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
110
use-cases/claude-code-plugin/scripts/test-save-memories.js
Executable file
110
use-cases/claude-code-plugin/scripts/test-save-memories.js
Executable file
@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Test script for saving memories to EverMem Cloud
|
||||
* Simulates typical Claude Code conversations
|
||||
*
|
||||
* Usage:
|
||||
* export EVERMEM_API_KEY="your-key"
|
||||
* node scripts/test-save-memories.js
|
||||
*/
|
||||
|
||||
import { getConfig, isConfigured } from '../hooks/scripts/utils/config.js';
|
||||
import { addMemory } from '../hooks/scripts/utils/evermem-api.js';
|
||||
|
||||
// Sample conversations that might happen in Claude Code
|
||||
const TEST_CONVERSATIONS = [
|
||||
{
|
||||
user: "I need to set up authentication for our API. What approach should we use?",
|
||||
assistant: "For your API authentication, I recommend using JWT (JSON Web Tokens) with short-lived access tokens (15 minutes) and longer-lived refresh tokens stored in httpOnly cookies. This provides a good balance of security and user experience. The refresh tokens in httpOnly cookies protect against XSS attacks, while the short access token lifetime limits the damage if a token is compromised."
|
||||
},
|
||||
{
|
||||
user: "We're getting a lot of N+1 query issues in the user listing endpoint",
|
||||
assistant: "I've identified the N+1 query problem. The issue is in the UserService.getAllUsers() method where we're loading user roles in a loop. I've fixed it by adding eager loading with a JOIN query. Changed from individual role queries per user to a single query with LEFT JOIN on the roles table. This reduced the query count from 101 queries to just 2 queries for 100 users."
|
||||
},
|
||||
{
|
||||
user: "What database should we use for this project?",
|
||||
assistant: "Based on your requirements for complex queries and transactions, I recommend PostgreSQL. It offers excellent support for JSON fields if you need schema flexibility, strong ACID compliance, and great performance for read-heavy workloads with proper indexing. We should also set up connection pooling with a max of 20 connections to handle concurrent requests efficiently."
|
||||
},
|
||||
{
|
||||
user: "The API is throwing rate limit errors in production",
|
||||
assistant: "I've implemented rate limiting using Redis with a sliding window algorithm. The configuration is: 100 requests per minute for authenticated users, 20 requests per minute for anonymous users. The rate limiter returns 429 Too Many Requests with a Retry-After header. I've also added the express-rate-limit middleware with the Redis store for distributed rate limiting across multiple server instances."
|
||||
},
|
||||
{
|
||||
user: "How should we handle errors in the API?",
|
||||
assistant: "I've set up a centralized error handling middleware. All errors now follow a consistent format: { status: 'error', code: 'ERROR_CODE', message: 'Human readable message', details: {} }. We use custom error classes (ValidationError, NotFoundError, AuthenticationError) that extend a base AppError. Unhandled errors are logged to our monitoring service and return a generic 500 response to avoid leaking implementation details."
|
||||
}
|
||||
];
|
||||
|
||||
async function main() {
|
||||
console.log('🧪 EverMem Save Memory Test\n');
|
||||
console.log('=' .repeat(50));
|
||||
|
||||
// Check configuration
|
||||
if (!isConfigured()) {
|
||||
console.error('❌ EVERMEM_API_KEY not set');
|
||||
console.error(' Run: export EVERMEM_API_KEY="your-key"');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = getConfig();
|
||||
console.log(`✓ API Key: ${config.apiKey.slice(0, 10)}...`);
|
||||
console.log(`✓ User ID: ${config.userId}`);
|
||||
console.log(`✓ Group ID: ${config.groupId}`);
|
||||
console.log(`✓ API URL: ${config.apiBaseUrl}`);
|
||||
console.log('=' .repeat(50));
|
||||
console.log('');
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
for (let i = 0; i < TEST_CONVERSATIONS.length; i++) {
|
||||
const conv = TEST_CONVERSATIONS[i];
|
||||
console.log(`\n📝 Conversation ${i + 1}/${TEST_CONVERSATIONS.length}`);
|
||||
console.log(` User: "${conv.user.slice(0, 50)}..."`);
|
||||
|
||||
// Save user message
|
||||
try {
|
||||
const userResult = await addMemory({
|
||||
content: conv.user,
|
||||
role: 'user',
|
||||
messageId: `test_user_${Date.now()}_${i}`
|
||||
});
|
||||
console.log(` ✓ User message saved (status: ${userResult.status || 'ok'})`);
|
||||
successCount++;
|
||||
} catch (error) {
|
||||
console.log(` ❌ User message failed: ${error.message}`);
|
||||
if (error.response) {
|
||||
console.log(` Response: ${JSON.stringify(error.response)}`);
|
||||
}
|
||||
failCount++;
|
||||
}
|
||||
|
||||
// Save assistant message
|
||||
try {
|
||||
const assistantResult = await addMemory({
|
||||
content: conv.assistant,
|
||||
role: 'assistant',
|
||||
messageId: `test_assistant_${Date.now()}_${i}`
|
||||
});
|
||||
console.log(` ✓ Assistant message saved (status: ${assistantResult.status || 'ok'})`);
|
||||
successCount++;
|
||||
} catch (error) {
|
||||
console.log(` ❌ Assistant message failed: ${error.message}`);
|
||||
if (error.response) {
|
||||
console.log(` Response: ${JSON.stringify(error.response)}`);
|
||||
}
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n' + '=' .repeat(50));
|
||||
console.log(`\n✅ Done! ${successCount} saved, ${failCount} failed`);
|
||||
console.log('\nNow run the retrieval test:');
|
||||
console.log(' node scripts/test-retrieve-memories.js');
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
console.error('Fatal error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user