claude-memory-agent 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +206 -206
  2. package/agent_card.py +186 -0
  3. package/bin/cli.js +327 -185
  4. package/bin/lib/banner.js +39 -0
  5. package/bin/lib/environment.js +166 -0
  6. package/bin/lib/installer.js +291 -0
  7. package/bin/lib/models.js +95 -0
  8. package/bin/lib/steps/advanced.js +101 -0
  9. package/bin/lib/steps/confirm.js +87 -0
  10. package/bin/lib/steps/model.js +57 -0
  11. package/bin/lib/steps/provider.js +65 -0
  12. package/bin/lib/steps/scope.js +59 -0
  13. package/bin/lib/steps/server.js +74 -0
  14. package/bin/lib/ui.js +75 -0
  15. package/bin/onboarding.js +164 -0
  16. package/bin/postinstall.js +35 -270
  17. package/config.py +103 -4
  18. package/dashboard.html +4902 -2689
  19. package/hooks/extract_memories.py +439 -0
  20. package/hooks/grounding-hook.py +422 -348
  21. package/hooks/pre_compact_hook.py +76 -0
  22. package/hooks/session_end.py +293 -192
  23. package/hooks/session_end_hook.py +149 -0
  24. package/hooks/session_start.py +227 -227
  25. package/hooks/stop_hook.py +372 -0
  26. package/install.py +972 -902
  27. package/main.py +5240 -2859
  28. package/mcp_server.py +451 -0
  29. package/package.json +58 -47
  30. package/requirements.txt +12 -8
  31. package/services/__init__.py +50 -50
  32. package/services/adaptive_ranker.py +272 -0
  33. package/services/agent_catalog.json +153 -0
  34. package/services/agent_registry.py +245 -730
  35. package/services/claude_md_sync.py +320 -4
  36. package/services/consolidation.py +417 -0
  37. package/services/curator.py +1606 -0
  38. package/services/database.py +4118 -2485
  39. package/services/embedding_pipeline.py +262 -0
  40. package/services/embeddings.py +493 -85
  41. package/services/memory_decay.py +408 -0
  42. package/services/native_memory_paths.py +86 -0
  43. package/services/native_memory_sync.py +496 -0
  44. package/services/response_manager.py +183 -0
  45. package/services/terminal_ui.py +199 -0
  46. package/services/tier_manager.py +235 -0
  47. package/services/websocket.py +26 -6
  48. package/skills/__init__.py +21 -1
  49. package/skills/confidence_tracker.py +441 -0
  50. package/skills/context.py +675 -0
  51. package/skills/curator.py +348 -0
  52. package/skills/search.py +444 -213
  53. package/skills/session_review.py +605 -0
  54. package/skills/store.py +484 -179
  55. package/terminal_dashboard.py +474 -0
  56. package/update_system.py +829 -817
  57. package/hooks/__pycache__/auto-detect-response.cpython-312.pyc +0 -0
  58. package/hooks/__pycache__/auto_capture.cpython-312.pyc +0 -0
  59. package/hooks/__pycache__/session_end.cpython-312.pyc +0 -0
  60. package/hooks/__pycache__/session_start.cpython-312.pyc +0 -0
  61. package/services/__pycache__/__init__.cpython-312.pyc +0 -0
  62. package/services/__pycache__/agent_registry.cpython-312.pyc +0 -0
  63. package/services/__pycache__/auth.cpython-312.pyc +0 -0
  64. package/services/__pycache__/auto_inject.cpython-312.pyc +0 -0
  65. package/services/__pycache__/claude_md_sync.cpython-312.pyc +0 -0
  66. package/services/__pycache__/cleanup.cpython-312.pyc +0 -0
  67. package/services/__pycache__/compaction_flush.cpython-312.pyc +0 -0
  68. package/services/__pycache__/confidence.cpython-312.pyc +0 -0
  69. package/services/__pycache__/daily_log.cpython-312.pyc +0 -0
  70. package/services/__pycache__/database.cpython-312.pyc +0 -0
  71. package/services/__pycache__/embeddings.cpython-312.pyc +0 -0
  72. package/services/__pycache__/insights.cpython-312.pyc +0 -0
  73. package/services/__pycache__/llm_analyzer.cpython-312.pyc +0 -0
  74. package/services/__pycache__/memory_md_sync.cpython-312.pyc +0 -0
  75. package/services/__pycache__/retry_queue.cpython-312.pyc +0 -0
  76. package/services/__pycache__/timeline.cpython-312.pyc +0 -0
  77. package/services/__pycache__/vector_index.cpython-312.pyc +0 -0
  78. package/services/__pycache__/websocket.cpython-312.pyc +0 -0
  79. package/skills/__pycache__/__init__.cpython-312.pyc +0 -0
  80. package/skills/__pycache__/admin.cpython-312.pyc +0 -0
  81. package/skills/__pycache__/checkpoint.cpython-312.pyc +0 -0
  82. package/skills/__pycache__/claude_md.cpython-312.pyc +0 -0
  83. package/skills/__pycache__/cleanup.cpython-312.pyc +0 -0
  84. package/skills/__pycache__/grounding.cpython-312.pyc +0 -0
  85. package/skills/__pycache__/insights.cpython-312.pyc +0 -0
  86. package/skills/__pycache__/natural_language.cpython-312.pyc +0 -0
  87. package/skills/__pycache__/retrieve.cpython-312.pyc +0 -0
  88. package/skills/__pycache__/search.cpython-312.pyc +0 -0
  89. package/skills/__pycache__/state.cpython-312.pyc +0 -0
  90. package/skills/__pycache__/store.cpython-312.pyc +0 -0
  91. package/skills/__pycache__/summarize.cpython-312.pyc +0 -0
  92. package/skills/__pycache__/timeline.cpython-312.pyc +0 -0
  93. package/skills/__pycache__/verification.cpython-312.pyc +0 -0
  94. package/test_automation.py +0 -221
  95. package/test_complete.py +0 -338
  96. package/test_full.py +0 -322
  97. package/verify_db.py +0 -134
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+
3
+ const { confirm } = require('@inquirer/prompts');
4
+ const chalk = require('chalk');
5
+ const { stepHeader, printBox, TOTAL_STEPS } = require('../ui.js');
6
+
7
+ /**
8
+ * Step 7: Review & Install
9
+ * Displays a summary of all collected configuration and asks for confirmation.
10
+ *
11
+ * @param {{ scope: string, projectPath: string|null, provider: string, model: string,
12
+ * port: number, host: string, autoStart: boolean, logLevel?: string,
13
+ * dbPath?: string, authEnabled?: boolean, hotTierDays?: number,
14
+ * warmTierDays?: number, hooks?: string[] }} config
15
+ * @returns {boolean} true if user confirms, false to abort
16
+ */
17
+ async function promptConfirm(config) {
18
+ stepHeader(7, TOTAL_STEPS, 'Review & Install');
19
+
20
+ // Format scope display
21
+ const scopeLabels = { global: 'Global', project: 'Project-specific', both: 'Both' };
22
+ const scopeDisplay = scopeLabels[config.scope] || config.scope;
23
+
24
+ // Format binding display
25
+ const bindingDisplay = config.host === '127.0.0.1'
26
+ ? 'localhost only'
27
+ : 'all interfaces (0.0.0.0)';
28
+
29
+ // Format auto-start display
30
+ const autoStartDisplay = config.autoStart ? 'Yes' : 'No';
31
+
32
+ // Format log level (use default if not customized)
33
+ const logLevel = config.logLevel || 'INFO';
34
+
35
+ // Format auth display
36
+ const authDisplay = config.authEnabled ? 'Enabled' : 'Disabled';
37
+
38
+ // Format hooks display
39
+ const defaultHooks = ['session_start', 'grounding', 'session_end'];
40
+ const hooks = config.hooks || defaultHooks;
41
+ const hooksDisplay = hooks.join(', ');
42
+
43
+ const label = (text) => chalk.dim(text);
44
+ const value = (text) => chalk.white.bold(text);
45
+
46
+ const lines = [
47
+ `${label('Scope:')} ${value(scopeDisplay)}`,
48
+ ];
49
+
50
+ if (config.projectPath) {
51
+ lines.push(`${label('Project:')} ${value(config.projectPath)}`);
52
+ }
53
+
54
+ lines.push(
55
+ `${label('Provider:')} ${value(config.provider)}`,
56
+ `${label('Model:')} ${value(config.model)}`,
57
+ `${label('Port:')} ${value(String(config.port))}`,
58
+ `${label('Binding:')} ${value(bindingDisplay)}`,
59
+ `${label('Auto-start:')} ${value(autoStartDisplay)}`,
60
+ `${label('Log Level:')} ${value(logLevel)}`,
61
+ `${label('Auth:')} ${value(authDisplay)}`,
62
+ `${label('Hooks:')} ${value(hooksDisplay)}`,
63
+ );
64
+
65
+ if (config.dbPath) {
66
+ lines.push(`${label('DB Path:')} ${value(config.dbPath)}`);
67
+ }
68
+
69
+ if (config.hotTierDays) {
70
+ lines.push(`${label('Hot Tier:')} ${value(config.hotTierDays + ' days')}`);
71
+ }
72
+
73
+ if (config.warmTierDays) {
74
+ lines.push(`${label('Warm Tier:')} ${value(config.warmTierDays + ' days')}`);
75
+ }
76
+
77
+ printBox('Installation Summary', lines);
78
+
79
+ const proceed = await confirm({
80
+ message: 'Proceed with installation?',
81
+ default: true,
82
+ });
83
+
84
+ return proceed;
85
+ }
86
+
87
+ module.exports = { promptConfirm };
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ const { select, confirm } = require('@inquirer/prompts');
4
+ const chalk = require('chalk');
5
+ const { stepHeader, TOTAL_STEPS } = require('../ui.js');
6
+ const { getModelsByProvider, formatModelChoice } = require('../models.js');
7
+
8
+ /**
9
+ * Step 4: Embedding Model
10
+ * Prompts the user to choose an embedding model based on their selected provider.
11
+ *
12
+ * @param {'ollama'|'sentence-transformers'} provider
13
+ * @param {{ ollama: { running: boolean, models: string[] } }} env
14
+ * @returns {{ model: string, pullModel: boolean }}
15
+ */
16
+ async function promptModel(provider, env) {
17
+ stepHeader(4, TOTAL_STEPS, 'Embedding Model');
18
+
19
+ console.log(chalk.dim(' Models are ordered from lightest to most powerful:\n'));
20
+
21
+ const installedModels = (env.ollama && env.ollama.models) || [];
22
+ const models = getModelsByProvider(provider);
23
+
24
+ const choices = models.map((model) => ({
25
+ name: formatModelChoice(model, installedModels),
26
+ value: model.name,
27
+ }));
28
+
29
+ // Find and mark the recommended model as default
30
+ const recommendedIndex = models.findIndex((m) => m.recommended);
31
+ if (recommendedIndex !== -1) {
32
+ choices[recommendedIndex].default = true;
33
+ }
34
+
35
+ const defaultModel = recommendedIndex !== -1 ? models[recommendedIndex].name : undefined;
36
+
37
+ const model = await select({
38
+ message: 'Select an embedding model:',
39
+ choices,
40
+ default: defaultModel,
41
+ });
42
+
43
+ let pullModel = false;
44
+
45
+ if (provider === 'ollama' && !installedModels.includes(model)) {
46
+ console.log(chalk.yellow('\n This model is not installed in Ollama.'));
47
+
48
+ pullModel = await confirm({
49
+ message: `Download it now? (ollama pull ${model})`,
50
+ default: true,
51
+ });
52
+ }
53
+
54
+ return { model, pullModel };
55
+ }
56
+
57
+ module.exports = { promptModel };
@@ -0,0 +1,65 @@
1
+ 'use strict';
2
+
3
+ const { select, confirm } = require('@inquirer/prompts');
4
+ const chalk = require('chalk');
5
+ const { stepHeader, printBox, TOTAL_STEPS } = require('../ui.js');
6
+
7
+ /**
8
+ * Step 3: Embedding Provider
9
+ * Prompts the user to choose between Ollama and sentence-transformers.
10
+ *
11
+ * @param {{ ollama: { running: boolean, models: string[] } }} env
12
+ * @returns {{ provider: 'ollama'|'sentence-transformers' }}
13
+ */
14
+ async function promptProvider(env) {
15
+ stepHeader(3, TOTAL_STEPS, 'Embedding Provider');
16
+
17
+ const ollamaRunning = env.ollama && env.ollama.running;
18
+ const ollamaStatus = ollamaRunning
19
+ ? chalk.green(' (running)')
20
+ : chalk.yellow(' (not detected)');
21
+
22
+ let provider = null;
23
+
24
+ while (provider === null) {
25
+ const choice = await select({
26
+ message: 'Which embedding provider would you like to use?',
27
+ choices: [
28
+ {
29
+ name: `Standalone (sentence-transformers) - runs locally, no external service needed`,
30
+ value: 'sentence-transformers',
31
+ },
32
+ {
33
+ name: `Ollama - uses Ollama for embeddings${ollamaStatus}`,
34
+ value: 'ollama',
35
+ },
36
+ ],
37
+ });
38
+
39
+ if (choice === 'ollama' && !ollamaRunning) {
40
+ printBox('Warning', [
41
+ chalk.yellow('Ollama is not running. To install:'),
42
+ '',
43
+ ' 1. Download from https://ollama.ai/download',
44
+ ' 2. Run: ollama serve',
45
+ ' 3. Then re-run this installer',
46
+ ]);
47
+
48
+ const continueAnyway = await confirm({
49
+ message: 'Continue with Ollama anyway?',
50
+ default: false,
51
+ });
52
+
53
+ if (!continueAnyway) {
54
+ // Re-prompt by continuing the loop
55
+ continue;
56
+ }
57
+ }
58
+
59
+ provider = choice;
60
+ }
61
+
62
+ return { provider };
63
+ }
64
+
65
+ module.exports = { promptProvider };
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+
3
+ const { select, input } = require('@inquirer/prompts');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const chalk = require('chalk');
7
+ const { stepHeader, TOTAL_STEPS } = require('../ui.js');
8
+
9
+ /**
10
+ * Step 2: Installation Scope
11
+ * Prompts the user to choose where the memory agent configuration is installed.
12
+ *
13
+ * @returns {{ scope: 'global'|'project'|'both', projectPath: string|null }}
14
+ */
15
+ async function promptScope() {
16
+ stepHeader(2, TOTAL_STEPS, 'Installation Scope');
17
+
18
+ const scope = await select({
19
+ message: 'Where should the memory agent be configured?',
20
+ choices: [
21
+ {
22
+ name: 'Global (recommended)',
23
+ value: 'global',
24
+ description: 'Configures ~/.claude/settings.json',
25
+ },
26
+ {
27
+ name: 'Project-specific',
28
+ value: 'project',
29
+ description: 'Configures .claude/settings.local.json in your project',
30
+ },
31
+ {
32
+ name: 'Both',
33
+ value: 'both',
34
+ description: 'Global config + project-specific override',
35
+ },
36
+ ],
37
+ });
38
+
39
+ let projectPath = null;
40
+
41
+ if (scope === 'project' || scope === 'both') {
42
+ projectPath = await input({
43
+ message: 'Project directory path:',
44
+ default: process.cwd(),
45
+ validate: (value) => {
46
+ const resolved = path.resolve(value);
47
+ if (!fs.existsSync(resolved)) {
48
+ return `Directory does not exist: ${resolved}`;
49
+ }
50
+ return true;
51
+ },
52
+ });
53
+ projectPath = path.resolve(projectPath);
54
+ }
55
+
56
+ return { scope, projectPath };
57
+ }
58
+
59
+ module.exports = { promptScope };
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ const { input, select, confirm } = require('@inquirer/prompts');
4
+ const chalk = require('chalk');
5
+ const net = require('net');
6
+ const { stepHeader, TOTAL_STEPS } = require('../ui.js');
7
+
8
+ /**
9
+ * Check if a port is available by attempting to listen on it briefly.
10
+ *
11
+ * @param {number} port
12
+ * @returns {Promise<boolean>} true if the port is available
13
+ */
14
+ function isPortAvailable(port) {
15
+ return new Promise((resolve) => {
16
+ const server = net.createServer();
17
+ server.once('error', () => resolve(false));
18
+ server.once('listening', () => {
19
+ server.close(() => resolve(true));
20
+ });
21
+ server.listen(port, '127.0.0.1');
22
+ });
23
+ }
24
+
25
+ /**
26
+ * Step 5: Server Settings
27
+ * Prompts the user for port, host binding, and auto-start preference.
28
+ *
29
+ * @returns {{ port: number, host: string, autoStart: boolean }}
30
+ */
31
+ async function promptServer() {
32
+ stepHeader(5, TOTAL_STEPS, 'Server Settings');
33
+
34
+ const portStr = await input({
35
+ message: 'Server port:',
36
+ default: '8102',
37
+ validate: async (value) => {
38
+ const num = Number(value);
39
+ if (!Number.isInteger(num) || num < 1 || num > 65535) {
40
+ return 'Port must be a number between 1 and 65535';
41
+ }
42
+ const available = await isPortAvailable(num);
43
+ if (!available) {
44
+ return `Port ${num} is already in use`;
45
+ }
46
+ return true;
47
+ },
48
+ });
49
+
50
+ const port = Number(portStr);
51
+
52
+ const host = await select({
53
+ message: 'Dashboard binding:',
54
+ choices: [
55
+ {
56
+ name: 'Localhost only (127.0.0.1) - Secure, recommended',
57
+ value: '127.0.0.1',
58
+ },
59
+ {
60
+ name: 'All interfaces (0.0.0.0) - Access from other devices',
61
+ value: '0.0.0.0',
62
+ },
63
+ ],
64
+ });
65
+
66
+ const autoStart = await confirm({
67
+ message: 'Start the agent after installation?',
68
+ default: true,
69
+ });
70
+
71
+ return { port, host, autoStart };
72
+ }
73
+
74
+ module.exports = { promptServer };
package/bin/lib/ui.js ADDED
@@ -0,0 +1,75 @@
1
+ 'use strict';
2
+
3
+ const chalk = require('chalk');
4
+ const boxen = require('boxen');
5
+
6
+ /**
7
+ * Total number of steps in the onboarding wizard.
8
+ */
9
+ const TOTAL_STEPS = 7;
10
+
11
+ /**
12
+ * Print a step header for the wizard.
13
+ * Renders as:
14
+ *
15
+ * Step 3/7 -- Embedding Model
16
+ * ----------------------------------------
17
+ *
18
+ * @param {number} step - Current step number
19
+ * @param {number} total - Total number of steps
20
+ * @param {string} title - Step title
21
+ */
22
+ function stepHeader(step, total, title) {
23
+ console.log('');
24
+ console.log(' ' + chalk.bold.cyan('Step ' + step + '/' + total) + chalk.bold(' \u2014 ' + title));
25
+ console.log(' ' + chalk.dim('\u2500'.repeat(40)));
26
+ console.log('');
27
+ }
28
+
29
+ /**
30
+ * Return a star rating string (filled + empty, out of 5), colored yellow.
31
+ * @param {number} count - Number of filled stars
32
+ * @returns {string}
33
+ */
34
+ function stars(count) {
35
+ const filled = '\u2605'.repeat(Math.min(count, 5));
36
+ const empty = '\u2606'.repeat(Math.max(5 - count, 0));
37
+ return chalk.yellow(filled + empty);
38
+ }
39
+
40
+ /**
41
+ * Print a bordered box with a title and content lines.
42
+ * Uses the boxen package for drawing.
43
+ *
44
+ * @param {string} title - Box title
45
+ * @param {string[]} lines - Array of content lines to display inside the box
46
+ */
47
+ function printBox(title, lines) {
48
+ const content = lines.join('\n');
49
+ const box = boxen(content, {
50
+ title: title,
51
+ titleAlignment: 'left',
52
+ padding: 1,
53
+ margin: { top: 0, bottom: 0, left: 2, right: 0 },
54
+ borderColor: 'cyan',
55
+ borderStyle: 'round',
56
+ });
57
+ console.log(box);
58
+ }
59
+
60
+ /**
61
+ * Print a thin horizontal divider across the terminal width.
62
+ * Falls back to 60 columns if terminal width is not available.
63
+ */
64
+ function divider() {
65
+ const width = process.stdout.columns || 60;
66
+ console.log(chalk.dim('\u2500'.repeat(width)));
67
+ }
68
+
69
+ module.exports = {
70
+ TOTAL_STEPS,
71
+ stepHeader,
72
+ stars,
73
+ printBox,
74
+ divider,
75
+ };
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const path = require('path');
5
+ const { spawn } = require('child_process');
6
+
7
+ const AGENT_DIR = path.resolve(path.dirname(__dirname));
8
+
9
+ async function runWizard() {
10
+ // Import lib modules
11
+ const { printBanner } = require('./lib/banner');
12
+ const { detectEnvironment, printEnvironment } = require('./lib/environment');
13
+ const { stepHeader, TOTAL_STEPS } = require('./lib/ui');
14
+ const { promptScope } = require('./lib/steps/scope');
15
+ const { promptProvider } = require('./lib/steps/provider');
16
+ const { promptModel } = require('./lib/steps/model');
17
+ const { promptServer } = require('./lib/steps/server');
18
+ const { promptAdvanced } = require('./lib/steps/advanced');
19
+ const { promptConfirm } = require('./lib/steps/confirm');
20
+ const { runInstaller } = require('./lib/installer');
21
+
22
+ // Screen 1: Banner + Environment Detection
23
+ printBanner();
24
+ stepHeader(1, TOTAL_STEPS, 'Environment Detection');
25
+ const env = await detectEnvironment();
26
+ printEnvironment(env);
27
+
28
+ console.log(''); // spacing
29
+
30
+ // Screen 2: Installation Scope
31
+ const { scope, projectPath } = await promptScope();
32
+
33
+ // Screen 3: Embedding Provider
34
+ const { provider } = await promptProvider(env);
35
+
36
+ // Screen 4: Model Selection
37
+ const { model, pullModel } = await promptModel(provider, env);
38
+
39
+ // Pull Ollama model if needed
40
+ if (pullModel && provider === 'ollama') {
41
+ const ora = require('ora');
42
+ const spinner = ora(`Downloading ${model}...`).start();
43
+ try {
44
+ const { execSync } = require('child_process');
45
+ execSync(`ollama pull ${model}`, { stdio: ['pipe', 'pipe', 'pipe'], timeout: 300000 });
46
+ spinner.succeed(`Downloaded ${model}`);
47
+ } catch (e) {
48
+ spinner.fail(`Failed to download ${model}. You can run "ollama pull ${model}" manually later.`);
49
+ }
50
+ }
51
+
52
+ // Screen 5: Server Settings
53
+ const { port, host, autoStart } = await promptServer();
54
+
55
+ // Screen 6: Advanced Settings
56
+ const advancedResult = await promptAdvanced();
57
+
58
+ // Build final config
59
+ const config = {
60
+ scope,
61
+ projectPath,
62
+ provider,
63
+ model,
64
+ port,
65
+ host,
66
+ autoStart,
67
+ logLevel: advancedResult ? advancedResult.logLevel : 'INFO',
68
+ dbPath: advancedResult ? advancedResult.dbPath : null,
69
+ authEnabled: advancedResult ? advancedResult.authEnabled : false,
70
+ hotTierDays: advancedResult ? advancedResult.hotTierDays : 14,
71
+ warmTierDays: advancedResult ? advancedResult.warmTierDays : 90,
72
+ hooks: advancedResult ? advancedResult.hooks : ['session_start', 'grounding', 'session_end'],
73
+ };
74
+
75
+ // Screen 7: Review & Confirm
76
+ const confirmed = await promptConfirm(config);
77
+ if (!confirmed) {
78
+ console.log('\n Installation cancelled.\n');
79
+ process.exit(0);
80
+ }
81
+
82
+ // Run Installation
83
+ console.log('');
84
+ const result = await runInstaller(config, AGENT_DIR);
85
+
86
+ if (result.success) {
87
+ const chalk = require('chalk');
88
+ const boxen = require('boxen');
89
+
90
+ const completionMsg = [
91
+ '',
92
+ chalk.green.bold(' Installation Complete!'),
93
+ '',
94
+ ` Dashboard: ${chalk.cyan(`http://localhost:${port}/dashboard`)}`,
95
+ ` Health: ${chalk.cyan(`http://localhost:${port}/health`)}`,
96
+ '',
97
+ chalk.dim(' Quick commands:'),
98
+ ` ${chalk.bold('claude-memory-agent status')} - Check agent status`,
99
+ ` ${chalk.bold('claude-memory-agent dashboard')} - Open dashboard`,
100
+ ` ${chalk.bold('claude-memory-agent doctor')} - Diagnose issues`,
101
+ ` ${chalk.bold('claude-memory-agent logs')} - View logs`,
102
+ '',
103
+ ].join('\n');
104
+
105
+ console.log(boxen(completionMsg, {
106
+ padding: 1,
107
+ margin: 1,
108
+ borderStyle: 'round',
109
+ borderColor: 'green',
110
+ }));
111
+ } else {
112
+ console.log('\n Installation completed with errors:');
113
+ result.errors.forEach(e => console.log(` - ${e}`));
114
+ console.log('');
115
+ }
116
+ }
117
+
118
+ // Non-interactive fallback for CI/piped
119
+ function runDefaults() {
120
+ const { runInstaller } = require('./lib/installer');
121
+ const config = {
122
+ scope: 'global',
123
+ projectPath: null,
124
+ provider: 'sentence-transformers',
125
+ model: 'BAAI/bge-base-en-v1.5',
126
+ port: 8102,
127
+ host: '127.0.0.1',
128
+ autoStart: false,
129
+ logLevel: 'INFO',
130
+ dbPath: null,
131
+ authEnabled: false,
132
+ hotTierDays: 14,
133
+ warmTierDays: 90,
134
+ hooks: ['session_start', 'grounding', 'session_end'],
135
+ };
136
+
137
+ return runInstaller(config, AGENT_DIR);
138
+ }
139
+
140
+ // Main entry
141
+ async function main() {
142
+ try {
143
+ if (!process.stdin.isTTY) {
144
+ // Non-interactive: use defaults silently
145
+ await runDefaults();
146
+ } else {
147
+ await runWizard();
148
+ }
149
+ } catch (err) {
150
+ if (err.name === 'ExitPromptError') {
151
+ // User pressed Ctrl+C
152
+ console.log('\n Setup cancelled.\n');
153
+ process.exit(0);
154
+ }
155
+ console.error('Setup error:', err.message);
156
+ process.exit(1);
157
+ }
158
+ }
159
+
160
+ if (require.main === module) {
161
+ main();
162
+ }
163
+
164
+ module.exports = { main, runWizard, runDefaults };