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.
117 lines
3.3 KiB
JavaScript
117 lines
3.3 KiB
JavaScript
/**
|
|
* Configuration loader for EverMem plugin
|
|
* Reads settings from .env file and environment variables
|
|
*/
|
|
|
|
import { readFileSync, existsSync } from 'fs';
|
|
import { resolve, dirname } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import { createHash } from 'crypto';
|
|
|
|
// Load .env file from plugin root
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const envPath = resolve(__dirname, '../../../.env');
|
|
|
|
if (existsSync(envPath)) {
|
|
const envContent = readFileSync(envPath, 'utf8');
|
|
for (const line of envContent.split('\n')) {
|
|
const trimmed = line.trim();
|
|
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
const [key, ...valueParts] = trimmed.split('=');
|
|
if (key && valueParts.length > 0) {
|
|
const value = valueParts.join('=').replace(/^["']|["']$/g, '');
|
|
if (!process.env[key]) { // Don't override existing env vars
|
|
process.env[key] = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const API_BASE_URL = 'https://api.evermind.ai';
|
|
|
|
/**
|
|
* Get the EverMem API key from environment
|
|
* @returns {string|null} API key or null if not set
|
|
*/
|
|
export function getApiKey() {
|
|
return process.env.EVERMEM_API_KEY || null;
|
|
}
|
|
|
|
/**
|
|
* Get the user ID for memory operations
|
|
* Defaults to 'claude-code-user' if not set
|
|
* @returns {string} User ID
|
|
*/
|
|
export function getUserId() {
|
|
return process.env.EVERMEM_USER_ID || 'claude-code-user';
|
|
}
|
|
|
|
/**
|
|
* Get the group ID for memory operations
|
|
* Uses project working directory as default group
|
|
* Format: {project_name_prefix_4}{path_hash_5} = 9 chars max
|
|
* @returns {string} Group ID
|
|
*/
|
|
export function getGroupId() {
|
|
if (process.env.EVERMEM_GROUP_ID) {
|
|
return process.env.EVERMEM_GROUP_ID;
|
|
}
|
|
// Use EVERMEM_CWD (set from hook input) or fall back to process.cwd()
|
|
const cwd = process.env.EVERMEM_CWD || process.cwd();
|
|
|
|
// Extract project name (last part of path)
|
|
const projectName = cwd.split('/').filter(Boolean).pop() || 'proj';
|
|
// Take first 4 chars of project name (lowercase, alphanumeric only)
|
|
const namePrefix = projectName.toLowerCase().replace(/[^a-z0-9]/g, '').substring(0, 4) || 'proj';
|
|
|
|
// Hash the full path and take first 5 chars
|
|
const pathHash = createHash('sha256').update(cwd).digest('hex').substring(0, 5);
|
|
|
|
// Combine: 4 chars name + 5 chars hash = 9 chars
|
|
return `${namePrefix}${pathHash}`;
|
|
}
|
|
|
|
/**
|
|
* Get the API base URL
|
|
* @returns {string} Base URL
|
|
*/
|
|
export function getApiBaseUrl() {
|
|
return process.env.EVERMEM_API_URL || API_BASE_URL;
|
|
}
|
|
|
|
/**
|
|
* Check if the plugin is properly configured
|
|
* @returns {boolean} True if API key is set
|
|
*/
|
|
export function isConfigured() {
|
|
return !!getApiKey();
|
|
}
|
|
|
|
/**
|
|
* Get a hashed identifier for the API key (for local storage association)
|
|
* Uses SHA-256 hash, truncated to 12 characters for compactness
|
|
* @returns {string|null} Key ID (first 12 chars of SHA-256 hash) or null if no API key
|
|
*/
|
|
export function getKeyId() {
|
|
const apiKey = getApiKey();
|
|
if (!apiKey) {
|
|
return null;
|
|
}
|
|
const hash = createHash('sha256').update(apiKey).digest('hex');
|
|
return hash.substring(0, 12);
|
|
}
|
|
|
|
/**
|
|
* Get full configuration object
|
|
* @returns {Object} Configuration
|
|
*/
|
|
export function getConfig() {
|
|
return {
|
|
apiKey: getApiKey(),
|
|
userId: getUserId(),
|
|
groupId: getGroupId(),
|
|
apiBaseUrl: getApiBaseUrl(),
|
|
isConfigured: isConfigured()
|
|
};
|
|
}
|