e975555896
* feat: Switch to persistent Chroma HTTP server Replace MCP subprocess approach with persistent Chroma HTTP server for improved performance and reliability. This re-enables Chroma on Windows by eliminating the subprocess spawning that caused console popups. Changes: - NEW: ChromaServerManager.ts - Manages local Chroma server lifecycle via `npx chroma run` - REFACTOR: ChromaSync.ts - Uses chromadb npm package's ChromaClient instead of MCP subprocess (removes Windows disabling) - UPDATE: worker-service.ts - Starts Chroma server on initialization - UPDATE: GracefulShutdown.ts - Stops Chroma server on shutdown - UPDATE: SettingsDefaultsManager.ts - New Chroma configuration options - UPDATE: build-hooks.js - Mark optional chromadb deps as external Benefits: - Eliminates subprocess spawn latency on first query - Single server process instead of per-operation subprocesses - No Python/uvx dependency for local mode - Re-enables Chroma vector search on Windows - Future-ready for cloud-hosted Chroma (claude-mem pro) - Cross-platform: Linux, macOS, Windows Configuration: CLAUDE_MEM_CHROMA_MODE=local|remote CLAUDE_MEM_CHROMA_HOST=127.0.0.1 CLAUDE_MEM_CHROMA_PORT=8000 CLAUDE_MEM_CHROMA_SSL=false Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Use chromadb v3.2.2 with v2 API heartbeat endpoint - Updated chromadb from ^1.9.2 to ^3.2.2 (includes CLI binary) - Changed heartbeat endpoint from /api/v1 to /api/v2 The 1.9.x version did not include the CLI, causing `npx chroma run` to fail. Version 3.2.2 includes the chroma CLI and uses the v2 API. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Add DefaultEmbeddingFunction for local vector embeddings - Added @chroma-core/default-embed dependency for local embeddings - Updated ChromaSync to use DefaultEmbeddingFunction with collections - Added isServerReachable() async method for reliable server detection - Fixed start() to detect and reuse existing Chroma servers - Updated build script to externalize native ONNX binaries - Added runtime dependency to plugin/package.json The embedding function uses all-MiniLM-L6-v2 model locally via ONNX, eliminating need for external embedding API calls. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Update src/services/sync/ChromaServerManager.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * fix: Remove duplicate else block from merge Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Add multi-tenancy support for claude-mem pro Wire tenant, database, and API key settings into ChromaSync for remote/pro mode. In remote mode: - Passes tenant and database to ChromaClient for data isolation - Adds Authorization header when API key is configured - Logs tenant isolation connection details Local mode unchanged - uses default_tenant without explicit params. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: add plugin.json to root .claude-plugin directory Claude Code's plugin discovery looks for plugin.json at the marketplace root level in .claude-plugin/, not nested inside plugin/.claude-plugin/. Without this file at the root level, skills and commands are not discovered. This matches the structure of working plugins like claude-research-team. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: resolve SDK spawn failures and sharp native binary crashes - Strip CLAUDECODE env var from SDK subprocesses to prevent "cannot be launched inside another Claude Code session" error (Claude Code 2.1.42+) - Lazy-load @chroma-core/default-embed to avoid eagerly pulling in sharp native binaries at bundle startup (fixes ERR_DLOPEN_FAILED) - Add stderr capture to SDK spawn for diagnosing future process failures - Exclude lockfiles from marketplace rsync and delete stale lockfiles before npm install to prevent native dep version mismatches Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: scaffold installer package with @clack/prompts and esbuild Sets up the claude-mem-installer project structure with build tooling, placeholder step and utility modules, and verified esbuild bundling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: implement entry point, welcome screen, and dependency checks Adds TTY guard, styled welcome banner with install mode selection, OS detection utilities, and automated dependency checking/installation for Node.js, git, Bun, and uv. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: implement IDE selection and AI provider configuration Adds multiselect IDE picker (Claude Code, Cursor) and provider configuration with Claude CLI/API, Gemini, and OpenRouter support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: implement settings configuration wizard and settings file writer Adds interactive settings wizard with default/custom modes, Chroma configuration, and a settings writer that merges with existing settings for upgrade support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: implement installation execution and worker startup Adds git clone, build, plugin registration (marketplace, cache, settings), and worker startup with health check polling. Fixes TypeScript errors in settings.ts validate callbacks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add completion screen and curl|bash bootstrap script Completion screen shows configuration summary and next steps. Bootstrap shell script enables curl -fsSL install.cmem.ai | bash with TTY reconnection for interactive prompts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: wire up full installer flow in index.ts Connects all steps: welcome → dependency checks → IDE selection → provider config → settings → installation → worker startup → completion. Configure-only mode skips clone/build/worker steps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add animated installer implementation plan Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: bigphoot <bigphoot@local> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Alexander Knigge <166455923+bigph00t@users.noreply.github.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: bigphoot <bigphoot@gmail.com>
175 lines
5.1 KiB
TypeScript
175 lines
5.1 KiB
TypeScript
import * as p from '@clack/prompts';
|
|
import pc from 'picocolors';
|
|
|
|
export interface SettingsConfig {
|
|
workerPort: string;
|
|
dataDir: string;
|
|
contextObservations: string;
|
|
logLevel: string;
|
|
pythonVersion: string;
|
|
chromaEnabled: boolean;
|
|
chromaMode?: 'local' | 'remote';
|
|
chromaHost?: string;
|
|
chromaPort?: string;
|
|
chromaSsl?: boolean;
|
|
}
|
|
|
|
export async function runSettingsConfiguration(): Promise<SettingsConfig> {
|
|
const useDefaults = await p.confirm({
|
|
message: 'Use default settings? (recommended for most users)',
|
|
initialValue: true,
|
|
});
|
|
|
|
if (p.isCancel(useDefaults)) {
|
|
p.cancel('Installation cancelled.');
|
|
process.exit(0);
|
|
}
|
|
|
|
if (useDefaults) {
|
|
return {
|
|
workerPort: '37777',
|
|
dataDir: '~/.claude-mem',
|
|
contextObservations: '50',
|
|
logLevel: 'INFO',
|
|
pythonVersion: '3.13',
|
|
chromaEnabled: true,
|
|
chromaMode: 'local',
|
|
};
|
|
}
|
|
|
|
// Custom settings
|
|
const workerPort = await p.text({
|
|
message: 'Worker service port:',
|
|
defaultValue: '37777',
|
|
placeholder: '37777',
|
|
validate: (value = '') => {
|
|
const port = parseInt(value, 10);
|
|
if (isNaN(port) || port < 1024 || port > 65535) {
|
|
return 'Port must be between 1024 and 65535';
|
|
}
|
|
},
|
|
});
|
|
if (p.isCancel(workerPort)) { p.cancel('Installation cancelled.'); process.exit(0); }
|
|
|
|
const dataDir = await p.text({
|
|
message: 'Data directory:',
|
|
defaultValue: '~/.claude-mem',
|
|
placeholder: '~/.claude-mem',
|
|
});
|
|
if (p.isCancel(dataDir)) { p.cancel('Installation cancelled.'); process.exit(0); }
|
|
|
|
const contextObservations = await p.text({
|
|
message: 'Number of context observations per session:',
|
|
defaultValue: '50',
|
|
placeholder: '50',
|
|
validate: (value = '') => {
|
|
const num = parseInt(value, 10);
|
|
if (isNaN(num) || num < 1 || num > 200) {
|
|
return 'Must be between 1 and 200';
|
|
}
|
|
},
|
|
});
|
|
if (p.isCancel(contextObservations)) { p.cancel('Installation cancelled.'); process.exit(0); }
|
|
|
|
const logLevel = await p.select({
|
|
message: 'Log level:',
|
|
options: [
|
|
{ value: 'DEBUG', label: 'DEBUG', hint: 'verbose' },
|
|
{ value: 'INFO', label: 'INFO', hint: 'default' },
|
|
{ value: 'WARN', label: 'WARN' },
|
|
{ value: 'ERROR', label: 'ERROR', hint: 'errors only' },
|
|
],
|
|
initialValue: 'INFO',
|
|
});
|
|
if (p.isCancel(logLevel)) { p.cancel('Installation cancelled.'); process.exit(0); }
|
|
|
|
const pythonVersion = await p.text({
|
|
message: 'Python version (for Chroma):',
|
|
defaultValue: '3.13',
|
|
placeholder: '3.13',
|
|
});
|
|
if (p.isCancel(pythonVersion)) { p.cancel('Installation cancelled.'); process.exit(0); }
|
|
|
|
const chromaEnabled = await p.confirm({
|
|
message: 'Enable Chroma vector search?',
|
|
initialValue: true,
|
|
});
|
|
if (p.isCancel(chromaEnabled)) { p.cancel('Installation cancelled.'); process.exit(0); }
|
|
|
|
let chromaMode: 'local' | 'remote' | undefined;
|
|
let chromaHost: string | undefined;
|
|
let chromaPort: string | undefined;
|
|
let chromaSsl: boolean | undefined;
|
|
|
|
if (chromaEnabled) {
|
|
const mode = await p.select({
|
|
message: 'Chroma mode:',
|
|
options: [
|
|
{ value: 'local' as const, label: 'Local', hint: 'starts local Chroma server' },
|
|
{ value: 'remote' as const, label: 'Remote', hint: 'connect to existing server' },
|
|
],
|
|
});
|
|
if (p.isCancel(mode)) { p.cancel('Installation cancelled.'); process.exit(0); }
|
|
chromaMode = mode;
|
|
|
|
if (mode === 'remote') {
|
|
const host = await p.text({
|
|
message: 'Chroma host:',
|
|
defaultValue: '127.0.0.1',
|
|
placeholder: '127.0.0.1',
|
|
});
|
|
if (p.isCancel(host)) { p.cancel('Installation cancelled.'); process.exit(0); }
|
|
chromaHost = host;
|
|
|
|
const port = await p.text({
|
|
message: 'Chroma port:',
|
|
defaultValue: '8000',
|
|
placeholder: '8000',
|
|
validate: (value = '') => {
|
|
const portNum = parseInt(value, 10);
|
|
if (isNaN(portNum) || portNum < 1 || portNum > 65535) return 'Port must be between 1 and 65535';
|
|
},
|
|
});
|
|
if (p.isCancel(port)) { p.cancel('Installation cancelled.'); process.exit(0); }
|
|
chromaPort = port;
|
|
|
|
const ssl = await p.confirm({
|
|
message: 'Use SSL for Chroma connection?',
|
|
initialValue: false,
|
|
});
|
|
if (p.isCancel(ssl)) { p.cancel('Installation cancelled.'); process.exit(0); }
|
|
chromaSsl = ssl;
|
|
}
|
|
}
|
|
|
|
const config: SettingsConfig = {
|
|
workerPort,
|
|
dataDir,
|
|
contextObservations,
|
|
logLevel,
|
|
pythonVersion,
|
|
chromaEnabled,
|
|
chromaMode,
|
|
chromaHost,
|
|
chromaPort,
|
|
chromaSsl,
|
|
};
|
|
|
|
// Show summary
|
|
const summaryLines = [
|
|
`Worker port: ${pc.cyan(workerPort)}`,
|
|
`Data directory: ${pc.cyan(dataDir)}`,
|
|
`Context observations: ${pc.cyan(contextObservations)}`,
|
|
`Log level: ${pc.cyan(logLevel)}`,
|
|
`Python version: ${pc.cyan(pythonVersion)}`,
|
|
`Chroma: ${chromaEnabled ? pc.green('enabled') : pc.dim('disabled')}`,
|
|
];
|
|
if (chromaEnabled && chromaMode) {
|
|
summaryLines.push(`Chroma mode: ${pc.cyan(chromaMode)}`);
|
|
}
|
|
|
|
p.note(summaryLines.join('\n'), 'Settings Summary');
|
|
|
|
return config;
|
|
}
|