* 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>
15 KiB
Comprehensive Claude-Mem Installer with @clack/prompts
Overview
Build a beautiful, animated CLI installer for claude-mem using @clack/prompts (v1.0.1). Distributable via npx claude-mem-installer and curl -fsSL https://install.cmem.ai | bash. Replaces the need for users to manually clone, build, configure settings, and start the worker.
Worktree: feat/animated-installer at .claude/worktrees/animated-installer
Phase 0: Documentation & API Reference
Allowed APIs (@clack/prompts v1.0.1, ESM-only)
| API | Signature | Use Case |
|---|---|---|
intro(title?) |
void |
Opening banner |
outro(message?) |
void |
Completion message |
cancel(message?) |
void |
User cancelled |
isCancel(value) |
boolean |
Check if user pressed Ctrl+C |
text(opts) |
Promise<string | symbol> |
API key input, port, data dir |
password(opts) |
Promise<string | symbol> |
API key input (masked) |
select(opts) |
Promise<Value | symbol> |
Provider, model, auth method |
multiselect(opts) |
Promise<Value[] | symbol> |
IDE selection, observation types |
confirm(opts) |
Promise<boolean | symbol> |
Enable Chroma, start worker |
spinner() |
SpinnerResult |
Installing deps, building, starting worker |
progress(opts) |
ProgressResult |
Multi-step installation progress |
tasks(tasks[]) |
Promise<void> |
Sequential install steps |
group(prompts, opts) |
Promise<Results> |
Chain prompts with shared results |
note(message, title) |
void |
Display settings summary, next steps |
log.info/success/warn/error(msg) |
void |
Status messages |
box(message, title, opts) |
void |
Welcome box, completion summary |
Anti-Patterns
- Do NOT use
require()— package is ESM-only - Do NOT call prompts without TTY check first — hangs indefinitely in non-TTY
- Do NOT forget
isCancel()check after every prompt (or usegroup()withonCancel) - Do NOT use
chalk— usepicocolors(clack's dep) for consistency text()has no numeric mode — validate manually for port numbersspinner.stop()does not accept status codes — usespinner.error()for failures
Distribution Patterns
- npx:
package.jsonbinfield →"./dist/index.js", file needs#!/usr/bin/env node - curl|bash: Shell bootstrap downloads JS, runs
node script.jsdirectly (preserves TTY) - esbuild: Bundle to single ESM file,
platform: 'node',bannerfor shebang
Key Source Files to Reference
- Settings defaults:
src/shared/SettingsDefaultsManager.ts(lines 73-125) - Settings validation:
src/services/server/SettingsRoutes.ts - Worker startup:
src/services/worker-service.ts(lines 337-359) - Health check:
src/services/infrastructure/HealthMonitor.ts - Plugin registration:
plugin/.claude-plugin/plugin.json,.claude-plugin/marketplace.json - Marketplace sync:
scripts/sync-marketplace.cjs - Cursor integration:
src/services/integrations/CursorHooksInstaller.ts - Existing OpenClaw installer:
install/public/openclaw.sh(reference for logic, not code to copy)
Phase 1: Project Scaffolding
Goal: Set up the installer package structure with build tooling.
Tasks
-
Create directory structure in the worktree:
installer/ ├── src/ │ ├── index.ts # Entry point with TTY guard │ ├── steps/ │ │ ├── welcome.ts # intro + version check │ │ ├── dependencies.ts # bun, uv, git checks │ │ ├── ide-selection.ts # IDE picker + registration │ │ ├── provider.ts # AI provider + API key │ │ ├── settings.ts # Additional settings config │ │ ├── install.ts # Clone, build, register plugin │ │ ├── worker.ts # Start worker + health check │ │ └── complete.ts # Summary + next steps │ └── utils/ │ ├── system.ts # OS detection, command runner │ ├── dependencies.ts # bun/uv/git install helpers │ └── settings-writer.ts # Write ~/.claude-mem/settings.json ├── build.mjs # esbuild config ├── package.json # bin, type: module, deps └── tsconfig.json -
Create
package.json:{ "name": "claude-mem-installer", "version": "1.0.0", "type": "module", "bin": { "claude-mem-installer": "./dist/index.js" }, "files": ["dist"], "scripts": { "build": "node build.mjs", "dev": "node build.mjs && node dist/index.js" }, "dependencies": { "@clack/prompts": "^1.0.1", "picocolors": "^1.1.1" }, "devDependencies": { "esbuild": "^0.24.0", "typescript": "^5.7.0", "@types/node": "^22.0.0" }, "engines": { "node": ">=18.0.0" } } -
Create
build.mjs:- esbuild bundle:
entryPoints: ['src/index.ts'],format: 'esm',platform: 'node',target: 'node18' - Banner:
#!/usr/bin/env node - Output:
dist/index.js
- esbuild bundle:
-
Create
tsconfig.json:module: "ESNext",target: "ES2022",moduleResolution: "bundler"
-
Run
npm installin installer/ directory
Verification
node build.mjssucceedsdist/index.jsexists with shebangnode dist/index.jsruns (even if empty installer)
Phase 2: Entry Point + Welcome Screen
Goal: Create the main entry point with TTY detection and a beautiful welcome screen.
Tasks
-
src/index.ts— Entry point:- TTY guard: if
!process.stdin.isTTY, print error directing user tonpx claude-mem-installer, exit 1 - Import and call
runInstaller()from steps - Top-level catch →
p.cancel()+ exit 1
- TTY guard: if
-
src/steps/welcome.ts— Welcome step:p.intro()with styled title using picocolors:" claude-mem installer "- Display version info via
p.log.info() - Check if already installed (detect
~/.claude-mem/settings.jsonand~/.claude/plugins/marketplaces/thedotmack/) - If upgrade detected,
p.confirm(): "claude-mem is already installed. Upgrade?" p.select()for install mode: Fresh Install vs Upgrade vs Configure Only
-
src/utils/system.ts— System utilities:detectOS(): returns 'macos' | 'linux' | 'windows'commandExists(cmd): checks if command is in PATHrunCommand(cmd, args): executes shell command, returns { stdout, stderr, exitCode }expandHome(path): resolves~to home directory
Verification
- Running
node dist/index.jsshows intro banner - Ctrl+C triggers cancel message
- Non-TTY (piped) shows error and exits
Phase 3: Dependency Checks
Goal: Check and install required dependencies (Bun, uv, git, Node.js version).
Tasks
-
src/steps/dependencies.ts— Dependency checker:- Use
p.tasks()to check each dependency sequentially with animated spinners:- Node.js: Verify >= 18.0.0 via
process.version - git:
commandExists('git'), show install instructions per OS if missing - Bun: Check PATH + common locations (
~/.bun/bin/bun,/usr/local/bin/bun,/opt/homebrew/bin/bun). Min version 1.1.14. Offer to auto-install fromhttps://bun.sh/install - uv: Check PATH + common locations (
~/.local/bin/uv,~/.cargo/bin/uv). Offer to auto-install fromhttps://astral.sh/uv/install.sh
- Node.js: Verify >= 18.0.0 via
- For missing deps:
p.confirm()to auto-install, or show manual instructions - After install attempts, re-verify each dep
- Use
-
src/utils/dependencies.ts— Install helpers:installBun(): downloads and runs bun install scriptinstallUv(): downloads and runs uv install scriptfindBinary(name, extraPaths[]): searches PATH + known locationscheckVersion(binary, minVersion): parses--versionoutput
Verification
- Shows green checkmarks for found dependencies
- Shows yellow warnings for missing deps with install option
- Auto-install actually installs bun/uv when confirmed
- Fails gracefully if git is missing (can't auto-install)
Phase 4: IDE Selection & Provider Configuration
Goal: Let user choose IDEs and configure AI provider with API keys.
Tasks
-
src/steps/ide-selection.ts— IDE picker:p.multiselect()with options:- Claude Code (default selected, hint: "recommended")
- Cursor
- Windsurf (hint: "coming soon", disabled: true)
- For Claude Code: explain plugin will be registered via marketplace
- For Cursor: explain hooks will be installed via CursorHooksInstaller pattern
- Store selections for later installation steps
-
src/steps/provider.ts— AI provider configuration:p.select()for provider:- Claude (hint: "recommended — uses your Claude subscription")
- Gemini (hint: "free tier available")
- OpenRouter (hint: "free models available")
- If Claude selected:
p.select()for auth method: "CLI (Max Plan subscription)" vs "API Key"- If API key:
p.password()for key input
- If Gemini selected:
p.password()for API key (required)p.select()for model: gemini-2.5-flash-lite (default), gemini-2.5-flash, gemini-3-flash-previewp.confirm()for rate limiting (default: true)
- If OpenRouter selected:
p.password()for API key (required)p.text()for model (default:xiaomi/mimo-v2-flash:free)
- Validate API keys where possible (non-empty, format check)
Verification
- Multiselect allows picking multiple IDEs
- Provider selection shows correct follow-up prompts
- API keys are masked during input
- Cancel at any step triggers graceful exit
Phase 5: Settings Configuration
Goal: Configure additional settings with sensible defaults.
Tasks
-
src/steps/settings.ts— Settings wizard:p.confirm(): "Use default settings?" (recommended) — if yes, skip detailed config- If customizing, use
p.group()for:- Worker port:
p.text()with default 37777, validate 1024-65535 - Data directory:
p.text()with default~/.claude-mem - Context observations:
p.text()with default 50, validate 1-200 - Log level:
p.select()— DEBUG, INFO (default), WARN, ERROR - Python version:
p.text()with default 3.13 - Chroma vector search:
p.confirm()(default: true)- If yes,
p.select()mode: local (default) vs remote - If remote:
p.text()for host, port,p.confirm()for SSL
- If yes,
- Worker port:
- Show settings summary via
p.note()before proceeding
-
src/utils/settings-writer.ts— Write settings:- Build flat key-value settings object matching SettingsDefaultsManager schema
- Merge with existing settings if upgrading (preserve user customizations)
- Write to
~/.claude-mem/settings.json - Create
~/.claude-mem/directory if it doesn't exist
Verification
- Default settings mode skips all detailed prompts
- Custom settings validates all inputs
- Settings file written matches SettingsDefaultsManager schema exactly
- Existing settings preserved on upgrade
Phase 6: Installation Execution
Goal: Clone repo, build plugin, register with IDEs, start worker.
Tasks
-
src/steps/install.ts— Installation runner:- Use
p.tasks()for visual progress:- "Cloning claude-mem repository":
git clone --depth 1 https://github.com/thedotmack/claude-mem.gitto temp dir - "Installing dependencies":
npm installin cloned repo - "Building plugin":
npm run buildin cloned repo - "Registering plugin": Copy plugin files to
~/.claude/plugins/marketplaces/thedotmack/- Create marketplace.json, plugin.json structure
- Register in
~/.claude/plugins/known_marketplaces.json - Add to
~/.claude/plugins/installed_plugins.json - Enable in
~/.claude/settings.jsonunderenabledPlugins
- "Installing dependencies" (in marketplace dir):
npm install
- "Cloning claude-mem repository":
- For Cursor (if selected):
- "Configuring Cursor hooks": Run Cursor hooks installer logic
- Write hooks.json to
~/.cursor/or project-level.cursor/ - Configure MCP in
.cursor/mcp.json
- Use
-
src/steps/worker.ts— Worker startup:- Use
p.spinner()for worker startup:- Start worker:
bun plugin/scripts/worker-service.cjs(from marketplace dir) - Write PID file to
~/.claude-mem/worker.pid
- Start worker:
- Two-stage health check (copy pattern from OpenClaw installer):
- Stage 1: Poll
/api/health— spinner message: "Starting worker service..." - Stage 2: Poll
/api/readiness— spinner message: "Initializing database..." - Budget: 30 attempts, 1 second apart
- On success:
spinner.stop("Worker running on port {port}") - On failure:
spinner.error("Worker failed to start"), show log path
- Stage 1: Poll
- Use
Verification
- Plugin files exist at
~/.claude/plugins/marketplaces/thedotmack/ - known_marketplaces.json updated
- installed_plugins.json updated
- settings.json has enabledPlugins entry
- Worker responds to
/api/healthwith 200 - Worker responds to
/api/readinesswith 200
Phase 7: Completion & Summary
Goal: Show success screen with configuration summary and next steps.
Tasks
src/steps/complete.ts— Completion screen:p.note()with configuration summary:- Provider + model
- IDEs configured
- Data directory
- Worker port
- Chroma enabled/disabled
p.note()with next steps:- "Open Claude Code and start a conversation — memory is automatic!"
- "View your memories: http://localhost:{port}"
- "Search past work: use /mem-search in Claude Code"
- If Cursor: "Open Cursor — hooks are active in your projects"
p.outro()with styled completion message
Verification
- Summary accurately reflects chosen settings
- URLs use correct port from settings
- Next steps are relevant to selected IDEs
Phase 8: curl|bash Bootstrap Script
Goal: Create the shell bootstrap script for curl -fsSL https://install.cmem.ai | bash.
Tasks
-
install/public/install.sh— Bootstrap script:- Check for Node.js >= 18 (required to run the installer)
- Download bundled installer JS to temp file
- Execute with
nodedirectly (preserves TTY for @clack/prompts) - Cleanup temp file on exit (trap)
- Support
--non-interactiveflag passthrough - Support
--provider=X --api-key=Yflag passthrough
-
Update
install/vercel.jsonto serveinstall.shalongsideopenclaw.sh
Verification
curl -fsSL https://install.cmem.ai | bashdownloads and runs installer- Interactive prompts work after curl download
- Temp file cleaned up on success and failure
- Flags pass through correctly
Phase 9: Final Verification
Checks
npm run buildin installer/ produces single-filedist/index.jsnode dist/index.jsruns full wizard flow- Fresh install on clean system works end-to-end
- Upgrade path preserves existing settings
- Ctrl+C at any step exits cleanly
- Non-TTY shows error message
- All settings written match SettingsDefaultsManager.ts defaults schema
- Worker health check succeeds after install
- Plugin appears in Claude Code plugin list
- grep for deprecated/non-existent APIs returns 0 results
- No
require()calls in source (ESM-only) - No
chalkimports (use picocolors)