memobank-cli 0.2.0 → 0.6.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.
- package/CHANGELOG.md +24 -0
- package/README.md +330 -83
- package/dist/cli.js +180 -13
- package/dist/cli.js.map +1 -1
- package/dist/commands/capture.d.ts +1 -0
- package/dist/commands/capture.d.ts.map +1 -1
- package/dist/commands/capture.js +38 -15
- package/dist/commands/capture.js.map +1 -1
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +94 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/install.d.ts +1 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +56 -4
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/lifecycle.d.ts +2 -0
- package/dist/commands/lifecycle.d.ts.map +1 -1
- package/dist/commands/lifecycle.js +10 -0
- package/dist/commands/lifecycle.js.map +1 -1
- package/dist/commands/migrate.d.ts +22 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +130 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/onboarding.d.ts +8 -7
- package/dist/commands/onboarding.d.ts.map +1 -1
- package/dist/commands/onboarding.js +411 -343
- package/dist/commands/onboarding.js.map +1 -1
- package/dist/commands/recall.d.ts +4 -2
- package/dist/commands/recall.d.ts.map +1 -1
- package/dist/commands/recall.js +27 -30
- package/dist/commands/recall.js.map +1 -1
- package/dist/commands/scan.d.ts +27 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +147 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/search.d.ts.map +1 -1
- package/dist/commands/search.js +7 -2
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/setup.js +2 -2
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/team.d.ts +18 -0
- package/dist/commands/team.d.ts.map +1 -0
- package/dist/commands/team.js +215 -0
- package/dist/commands/team.js.map +1 -0
- package/dist/commands/workspace.d.ts +12 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +189 -0
- package/dist/commands/workspace.js.map +1 -0
- package/dist/commands/write.d.ts.map +1 -1
- package/dist/commands/write.js +4 -0
- package/dist/commands/write.js.map +1 -1
- package/dist/components/MultiSelect.d.ts +17 -0
- package/dist/components/MultiSelect.d.ts.map +1 -0
- package/dist/components/MultiSelect.js +7 -0
- package/dist/components/MultiSelect.js.map +1 -0
- package/dist/config.d.ts +3 -15
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +28 -61
- package/dist/config.js.map +1 -1
- package/dist/core/decay-engine.d.ts +11 -0
- package/dist/core/decay-engine.d.ts.map +1 -1
- package/dist/core/decay-engine.js +11 -0
- package/dist/core/decay-engine.js.map +1 -1
- package/dist/core/embedding.d.ts +1 -1
- package/dist/core/embedding.d.ts.map +1 -1
- package/dist/core/embedding.js +13 -0
- package/dist/core/embedding.js.map +1 -1
- package/dist/core/lifecycle-manager.d.ts +16 -0
- package/dist/core/lifecycle-manager.d.ts.map +1 -1
- package/dist/core/lifecycle-manager.js +86 -0
- package/dist/core/lifecycle-manager.js.map +1 -1
- package/dist/core/reranker.d.ts +14 -0
- package/dist/core/reranker.d.ts.map +1 -0
- package/dist/core/reranker.js +64 -0
- package/dist/core/reranker.js.map +1 -0
- package/dist/core/retriever.d.ts +2 -5
- package/dist/core/retriever.d.ts.map +1 -1
- package/dist/core/retriever.js +96 -16
- package/dist/core/retriever.js.map +1 -1
- package/dist/core/sanitizer.d.ts +10 -0
- package/dist/core/sanitizer.d.ts.map +1 -1
- package/dist/core/sanitizer.js +57 -39
- package/dist/core/sanitizer.js.map +1 -1
- package/dist/core/store.d.ts +16 -30
- package/dist/core/store.d.ts.map +1 -1
- package/dist/core/store.js +121 -71
- package/dist/core/store.js.map +1 -1
- package/dist/engines/lancedb-engine.d.ts.map +1 -1
- package/dist/engines/lancedb-engine.js +2 -1
- package/dist/engines/lancedb-engine.js.map +1 -1
- package/dist/platforms/claude-code.d.ts +2 -1
- package/dist/platforms/claude-code.d.ts.map +1 -1
- package/dist/platforms/claude-code.js +21 -3
- package/dist/platforms/claude-code.js.map +1 -1
- package/dist/platforms/gemini.d.ts +7 -0
- package/dist/platforms/gemini.d.ts.map +1 -0
- package/dist/platforms/gemini.js +109 -0
- package/dist/platforms/gemini.js.map +1 -0
- package/dist/platforms/qwen.d.ts +7 -0
- package/dist/platforms/qwen.d.ts.map +1 -0
- package/dist/platforms/qwen.js +109 -0
- package/dist/platforms/qwen.js.map +1 -0
- package/dist/types.d.ts +32 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +1 -1
- package/package.json +3 -2
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Onboarding command (memo init)
|
|
4
|
+
* 4-step interactive TUI setup wizard using Ink
|
|
5
|
+
*
|
|
6
|
+
* ink, ink-text-input, and ink-select-input are ESM-only packages that cannot be
|
|
7
|
+
* require()'d from a CommonJS bundle. All imports of those packages are done via
|
|
8
|
+
* a Function-constructor-based dynamic import() so TypeScript does not rewrite
|
|
9
|
+
* them to require() calls.
|
|
6
10
|
*/
|
|
7
11
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
12
|
if (k2 === undefined) k2 = k;
|
|
@@ -41,380 +45,444 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
41
45
|
exports.onboardingCommand = onboardingCommand;
|
|
42
46
|
const fs = __importStar(require("fs"));
|
|
43
47
|
const path = __importStar(require("path"));
|
|
44
|
-
const readline = __importStar(require("readline"));
|
|
45
48
|
const child_process_1 = require("child_process");
|
|
46
|
-
const util_1 = require("util");
|
|
47
49
|
const store_1 = require("../core/store");
|
|
48
50
|
const config_1 = require("../config");
|
|
49
|
-
const import_1 = require("./import");
|
|
50
51
|
const claude_code_1 = require("../platforms/claude-code");
|
|
51
52
|
const codex_1 = require("../platforms/codex");
|
|
53
|
+
const gemini_1 = require("../platforms/gemini");
|
|
54
|
+
const qwen_1 = require("../platforms/qwen");
|
|
52
55
|
const cursor_1 = require("../platforms/cursor");
|
|
53
|
-
const
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
process.stdout.write('\x1B[?25l');
|
|
73
|
-
const render = () => {
|
|
74
|
-
// Clear screen from current position
|
|
75
|
-
readline.clearScreenDown(process.stdout);
|
|
76
|
-
rl.write('\x1B[G'); // Move to beginning of line
|
|
77
|
-
console.log(`\n${title}\n`);
|
|
78
|
-
items.forEach((item, index) => {
|
|
79
|
-
const isSelected = index === selectedIndex;
|
|
80
|
-
const icon = isSelected ? '❯' : ' ';
|
|
81
|
-
const check = isSelected ? '◉' : '◯';
|
|
82
|
-
const style = isSelected ? '\x1B[36m' : '\x1B[90m'; // Cyan or Gray
|
|
83
|
-
const disabled = item.disabled ? '\x1B[90m' : '';
|
|
84
|
-
console.log(`${style}${icon} ${check} ${item.label}${item.description ? ` - ${item.description}` : ''}\x1B[39m`);
|
|
85
|
-
});
|
|
86
|
-
console.log('\n\x1B[90mUse ↑↓ arrows to navigate, Enter to select\x1B[39m');
|
|
87
|
-
};
|
|
88
|
-
const handleKeypress = (_, key) => {
|
|
89
|
-
if (key.name === 'up' && selectedIndex > 0) {
|
|
90
|
-
selectedIndex--;
|
|
91
|
-
render();
|
|
92
|
-
}
|
|
93
|
-
else if (key.name === 'down' && selectedIndex < items.length - 1) {
|
|
94
|
-
selectedIndex++;
|
|
95
|
-
render();
|
|
96
|
-
}
|
|
97
|
-
else if (key.name === 'return' && !items[selectedIndex].disabled) {
|
|
98
|
-
cleanup();
|
|
99
|
-
resolve(selectedIndex);
|
|
100
|
-
}
|
|
101
|
-
else if (key.name === 'c' && key.ctrl) {
|
|
102
|
-
cleanup();
|
|
103
|
-
process.exit(0);
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
const cleanup = () => {
|
|
107
|
-
// Show cursor
|
|
108
|
-
process.stdout.write('\x1B[?25h');
|
|
109
|
-
rl.removeListener('keypress', handleKeypress);
|
|
110
|
-
};
|
|
111
|
-
rl.on('keypress', handleKeypress);
|
|
112
|
-
render();
|
|
113
|
-
});
|
|
56
|
+
const workspace_1 = require("./workspace");
|
|
57
|
+
/** Test Ollama connectivity and model availability */
|
|
58
|
+
async function testOllamaConnection(baseUrl, model) {
|
|
59
|
+
try {
|
|
60
|
+
const url = baseUrl.replace(/\/$/, '');
|
|
61
|
+
const res = await fetch(`${url}/api/tags`);
|
|
62
|
+
if (!res.ok)
|
|
63
|
+
return `Ollama returned HTTP ${res.status}`;
|
|
64
|
+
const data = await res.json();
|
|
65
|
+
const models = data.models?.map((m) => m.name) ?? [];
|
|
66
|
+
const found = models.some((n) => n === model || n.startsWith(`${model}:`));
|
|
67
|
+
if (!found) {
|
|
68
|
+
return `Model "${model}" not found — run: ollama pull ${model}`;
|
|
69
|
+
}
|
|
70
|
+
return null; // success
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return `Cannot reach Ollama at ${baseUrl} — run: ollama serve`;
|
|
74
|
+
}
|
|
114
75
|
}
|
|
115
|
-
/**
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
console.log(`\n${title}\n`);
|
|
127
|
-
items.forEach((item, index) => {
|
|
128
|
-
const isSelected = index === selectedIndex;
|
|
129
|
-
const icon = isSelected ? '❯' : ' ';
|
|
130
|
-
const check = selected[index] ? '◉' : '◯';
|
|
131
|
-
const style = isSelected ? '\x1B[36m' : '\x1B[90m';
|
|
132
|
-
console.log(`${style}${icon} [${check}] ${item.label}\x1B[39m`);
|
|
133
|
-
});
|
|
134
|
-
console.log('\n\x1B[90m↑↓ navigate, Space to toggle, Enter to confirm\x1B[39m');
|
|
135
|
-
};
|
|
136
|
-
const handleKeypress = (_, key) => {
|
|
137
|
-
if (key.name === 'up' && selectedIndex > 0) {
|
|
138
|
-
selectedIndex--;
|
|
139
|
-
render();
|
|
140
|
-
}
|
|
141
|
-
else if (key.name === 'down' && selectedIndex < items.length - 1) {
|
|
142
|
-
selectedIndex++;
|
|
143
|
-
render();
|
|
144
|
-
}
|
|
145
|
-
else if (key.name === 'space') {
|
|
146
|
-
selected[selectedIndex] = !selected[selectedIndex];
|
|
147
|
-
render();
|
|
148
|
-
}
|
|
149
|
-
else if (key.name === 'return') {
|
|
150
|
-
cleanup();
|
|
151
|
-
resolve(items.filter((_, i) => selected[i]).map((i) => i.value));
|
|
152
|
-
}
|
|
153
|
-
else if (key.ctrl && key.name === 'c') {
|
|
154
|
-
cleanup();
|
|
155
|
-
process.exit(0);
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
const cleanup = () => {
|
|
159
|
-
process.stdout.write('\x1B[?25h');
|
|
160
|
-
rl.removeListener('keypress', handleKeypress);
|
|
161
|
-
};
|
|
162
|
-
rl.on('keypress', handleKeypress);
|
|
163
|
-
render();
|
|
164
|
-
});
|
|
76
|
+
/** Detect git repo name from cwd */
|
|
77
|
+
function detectProjectName() {
|
|
78
|
+
try {
|
|
79
|
+
const result = (0, child_process_1.execSync)('git rev-parse --show-toplevel', {
|
|
80
|
+
encoding: 'utf-8', stdio: 'pipe',
|
|
81
|
+
}).trim();
|
|
82
|
+
return path.basename(result);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return path.basename(process.cwd());
|
|
86
|
+
}
|
|
165
87
|
}
|
|
166
|
-
/**
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
88
|
+
/** Check if Claude Code has auto-memory explicitly disabled */
|
|
89
|
+
function isAutoMemoryDisabled() {
|
|
90
|
+
const settingsPath = path.join(process.env.HOME || process.env.USERPROFILE || '', '.claude', 'settings.json');
|
|
91
|
+
if (!fs.existsSync(settingsPath)) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
170
94
|
try {
|
|
171
|
-
const
|
|
172
|
-
return
|
|
95
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
96
|
+
return settings.autoMemoryEnabled === false;
|
|
173
97
|
}
|
|
174
98
|
catch {
|
|
175
|
-
return
|
|
99
|
+
return false;
|
|
176
100
|
}
|
|
177
101
|
}
|
|
178
|
-
/**
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
102
|
+
/** Detect which platforms are installed */
|
|
103
|
+
function detectPlatforms() {
|
|
104
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
105
|
+
const isInPath = (cmd) => {
|
|
106
|
+
try {
|
|
107
|
+
(0, child_process_1.execSync)(`which ${cmd}`, { stdio: 'pipe' });
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
return [
|
|
115
|
+
{
|
|
116
|
+
label: 'Claude Code',
|
|
117
|
+
value: 'claude-code',
|
|
118
|
+
hint: fs.existsSync(path.join(home, '.claude', 'settings.json')) ? '✓ detected' : 'not found',
|
|
119
|
+
disabled: false,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
label: 'Codex',
|
|
123
|
+
value: 'codex',
|
|
124
|
+
hint: isInPath('codex') ? '✓ detected' : 'not found',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
label: 'Gemini CLI',
|
|
128
|
+
value: 'gemini',
|
|
129
|
+
hint: (0, gemini_1.detectGemini)() ? '✓ detected' : 'not found',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
label: 'Qwen Code',
|
|
133
|
+
value: 'qwen',
|
|
134
|
+
hint: (0, qwen_1.detectQwen)() ? '✓ detected' : 'not found',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
label: 'Cursor',
|
|
138
|
+
value: 'cursor',
|
|
139
|
+
hint: fs.existsSync(path.join(process.cwd(), '.cursor')) ? '✓ detected' : 'not found',
|
|
140
|
+
},
|
|
141
|
+
];
|
|
187
142
|
}
|
|
188
|
-
/**
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
console.log('║ Persistent memory for AI coding sessions ║');
|
|
203
|
-
console.log('║ ║');
|
|
204
|
-
console.log('╚═══════════════════════════════════════════════════════════╝');
|
|
205
|
-
console.log('\x1B[39m\n');
|
|
206
|
-
// Detect project
|
|
207
|
-
const projectName = await detectProjectName(cwd);
|
|
208
|
-
console.log(`📁 Project: \x1B[36m${projectName}\x1B[39m`);
|
|
209
|
-
console.log(`📂 Location: \x1B[90m${repoRoot}\x1B[39m\n`);
|
|
210
|
-
// Initialize config if needed
|
|
211
|
-
const configPath = path.join(repoRoot, 'meta', 'config.yaml');
|
|
212
|
-
let config;
|
|
213
|
-
if (!fs.existsSync(configPath)) {
|
|
214
|
-
(0, config_1.initConfig)(repoRoot, projectName);
|
|
215
|
-
config = (0, config_1.loadConfig)(repoRoot);
|
|
216
|
-
console.log('✓ Created new configuration\n');
|
|
143
|
+
/** Get default-selected platform values (detected ones) */
|
|
144
|
+
function getDetectedPlatforms(items) {
|
|
145
|
+
return items.filter(i => i.hint?.includes('✓')).map(i => i.value);
|
|
146
|
+
}
|
|
147
|
+
async function runSetup(state, gitRoot) {
|
|
148
|
+
const repoRoot = path.join(gitRoot, state.projectDir);
|
|
149
|
+
const summaryLines = [];
|
|
150
|
+
let autoMemoryWarning = false;
|
|
151
|
+
// 1. Init config
|
|
152
|
+
(0, config_1.initConfig)(repoRoot, state.projectName);
|
|
153
|
+
// 2. Create directory structure
|
|
154
|
+
const TYPES = ['lesson', 'decision', 'workflow', 'architecture'];
|
|
155
|
+
for (const type of TYPES) {
|
|
156
|
+
fs.mkdirSync(path.join(repoRoot, type), { recursive: true });
|
|
217
157
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
158
|
+
summaryLines.push(`Memories: ${repoRoot}`);
|
|
159
|
+
// 3. Initialize workspace remote if provided
|
|
160
|
+
if (state.workspaceRemote.trim()) {
|
|
161
|
+
try {
|
|
162
|
+
await (0, workspace_1.workspaceInit)(state.workspaceRemote.trim(), repoRoot);
|
|
163
|
+
summaryLines.push(`Workspace: ${state.workspaceRemote.trim()}`);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
summaryLines.push(`⚠ Workspace init failed: ${err.message}`);
|
|
167
|
+
}
|
|
221
168
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
{ label: 'Exit', value: 'exit', description: 'Finish setup' },
|
|
231
|
-
];
|
|
232
|
-
let exitSetup = false;
|
|
233
|
-
while (!exitSetup) {
|
|
234
|
-
const choice = await showMenu(rl, 'What would you like to do?', mainMenuItems);
|
|
235
|
-
switch (mainMenuItems[choice].value) {
|
|
236
|
-
case 'quick':
|
|
237
|
-
await quickSetup(rl, repoRoot, config);
|
|
238
|
-
break;
|
|
239
|
-
case 'custom':
|
|
240
|
-
await customSetup(rl, repoRoot, config);
|
|
169
|
+
// 4. Install platform adapters
|
|
170
|
+
for (const platform of state.platforms) {
|
|
171
|
+
switch (platform) {
|
|
172
|
+
case 'claude-code':
|
|
173
|
+
await (0, claude_code_1.installClaudeCode)(repoRoot, state.enableAutoMemory);
|
|
174
|
+
if (!state.enableAutoMemory) {
|
|
175
|
+
autoMemoryWarning = true;
|
|
176
|
+
}
|
|
241
177
|
break;
|
|
242
|
-
case '
|
|
243
|
-
await (0,
|
|
178
|
+
case 'codex':
|
|
179
|
+
await (0, codex_1.installCodex)(process.cwd());
|
|
244
180
|
break;
|
|
245
|
-
case '
|
|
246
|
-
await
|
|
181
|
+
case 'gemini':
|
|
182
|
+
await (0, gemini_1.installGemini)();
|
|
247
183
|
break;
|
|
248
|
-
case '
|
|
249
|
-
await
|
|
184
|
+
case 'qwen':
|
|
185
|
+
await (0, qwen_1.installQwen)();
|
|
250
186
|
break;
|
|
251
|
-
case '
|
|
252
|
-
|
|
187
|
+
case 'cursor':
|
|
188
|
+
await (0, cursor_1.installCursor)(process.cwd());
|
|
253
189
|
break;
|
|
254
190
|
}
|
|
255
191
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
rl.close();
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* Quick setup - automated recommended configuration
|
|
262
|
-
*/
|
|
263
|
-
async function quickSetup(rl, repoRoot, config) {
|
|
264
|
-
console.log('\n🚀 Quick Setup\n');
|
|
265
|
-
// Check for Ollama
|
|
266
|
-
const hasOllama = await checkOllama();
|
|
267
|
-
if (hasOllama) {
|
|
268
|
-
console.log('✓ Detected Ollama installation');
|
|
269
|
-
config.embedding.engine = 'lancedb';
|
|
270
|
-
config.embedding.provider = 'ollama';
|
|
271
|
-
config.embedding.model = 'mxbai-embed-large';
|
|
272
|
-
config.embedding.dimensions = 1024;
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
console.log('⊘ Ollama not found, using text search');
|
|
276
|
-
config.embedding.engine = 'text';
|
|
192
|
+
if (state.platforms.length > 0) {
|
|
193
|
+
summaryLines.push(`Platforms: ${state.platforms.join(', ')}`);
|
|
277
194
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Custom setup - step by step configuration
|
|
289
|
-
*/
|
|
290
|
-
async function customSetup(rl, repoRoot, config) {
|
|
291
|
-
// Embedding choice
|
|
292
|
-
const embeddingMenu = [
|
|
293
|
-
{ label: 'Ollama (Local, Free)', value: 'ollama', description: 'Recommended' },
|
|
294
|
-
{ label: 'OpenAI (Cloud)', value: 'openai' },
|
|
295
|
-
{ label: 'Text Only (No embeddings)', value: 'text' },
|
|
296
|
-
];
|
|
297
|
-
const embeddingChoice = await showMenu(rl, 'Choose embedding provider:', embeddingMenu);
|
|
298
|
-
switch (embeddingMenu[embeddingChoice].value) {
|
|
299
|
-
case 'ollama':
|
|
300
|
-
config.embedding.engine = 'lancedb';
|
|
195
|
+
// 6. Update engine config if lancedb
|
|
196
|
+
if (state.searchEngine === 'lancedb') {
|
|
197
|
+
const config = (0, config_1.loadConfig)(repoRoot);
|
|
198
|
+
config.embedding.engine = 'lancedb';
|
|
199
|
+
if (state.embeddingProvider === 'ollama') {
|
|
200
|
+
const ollamaUrl = state.embeddingUrl || 'http://localhost:11434';
|
|
201
|
+
const ollamaModel = state.embeddingModel || 'mxbai-embed-large';
|
|
301
202
|
config.embedding.provider = 'ollama';
|
|
302
|
-
config.embedding.
|
|
203
|
+
config.embedding.base_url = ollamaUrl;
|
|
204
|
+
config.embedding.model = ollamaModel;
|
|
303
205
|
config.embedding.dimensions = 1024;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
206
|
+
// Test connectivity
|
|
207
|
+
const ollamaErr = await testOllamaConnection(ollamaUrl, ollamaModel);
|
|
208
|
+
if (ollamaErr) {
|
|
209
|
+
summaryLines.push(`⚠ Ollama: ${ollamaErr}`);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
summaryLines.push(`✓ Ollama connected, model "${ollamaModel}" ready`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (state.embeddingProvider === 'openai') {
|
|
307
216
|
config.embedding.provider = 'openai';
|
|
308
217
|
config.embedding.model = 'text-embedding-3-small';
|
|
309
218
|
config.embedding.dimensions = 1536;
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Platform-specific setup
|
|
338
|
-
*/
|
|
339
|
-
async function platformSetup(rl, repoRoot) {
|
|
340
|
-
const platformItems = [
|
|
341
|
-
{ label: 'Claude Code', value: 'claude', selected: false },
|
|
342
|
-
{ label: 'Cursor', value: 'cursor', selected: false },
|
|
343
|
-
{ label: 'Codex (AGENTS.md)', value: 'codex', selected: false },
|
|
344
|
-
];
|
|
345
|
-
const selected = await showCheckbox(rl, 'Select platforms to configure:', platformItems);
|
|
346
|
-
console.log('\n📦 Installing...\n');
|
|
347
|
-
if (selected.includes('claude')) {
|
|
348
|
-
await (0, claude_code_1.installClaudeCode)(repoRoot);
|
|
349
|
-
}
|
|
350
|
-
if (selected.includes('cursor')) {
|
|
351
|
-
await (0, cursor_1.installCursor)(process.cwd());
|
|
219
|
+
// Save API key to env file (not config.yaml for security)
|
|
220
|
+
if (state.embeddingApiKey.trim()) {
|
|
221
|
+
const envPath = path.join(repoRoot, '.env');
|
|
222
|
+
const envLine = `OPENAI_API_KEY=${state.embeddingApiKey.trim()}\n`;
|
|
223
|
+
const existing = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf-8') : '';
|
|
224
|
+
if (!existing.includes('OPENAI_API_KEY=')) {
|
|
225
|
+
fs.writeFileSync(envPath, existing + envLine, 'utf-8');
|
|
226
|
+
summaryLines.push(`OpenAI API key saved to ${envPath}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
else if (state.embeddingProvider === 'jina') {
|
|
231
|
+
config.embedding.provider = 'jina';
|
|
232
|
+
config.embedding.model = 'jina-embeddings-v3';
|
|
233
|
+
config.embedding.dimensions = 1024;
|
|
234
|
+
if (state.embeddingApiKey.trim()) {
|
|
235
|
+
const envPath = path.join(repoRoot, '.env');
|
|
236
|
+
const envLine = `JINA_API_KEY=${state.embeddingApiKey.trim()}\n`;
|
|
237
|
+
const existing = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf-8') : '';
|
|
238
|
+
if (!existing.includes('JINA_API_KEY=')) {
|
|
239
|
+
fs.writeFileSync(envPath, existing + envLine, 'utf-8');
|
|
240
|
+
summaryLines.push(`Jina API key saved to ${envPath}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
(0, config_1.writeConfig)(repoRoot, config);
|
|
352
245
|
}
|
|
353
|
-
if (
|
|
354
|
-
|
|
246
|
+
if (state.enableReranker && state.rerankerProvider) {
|
|
247
|
+
const config = (0, config_1.loadConfig)(repoRoot);
|
|
248
|
+
config.reranker = {
|
|
249
|
+
enabled: true,
|
|
250
|
+
provider: state.rerankerProvider,
|
|
251
|
+
};
|
|
252
|
+
(0, config_1.writeConfig)(repoRoot, config);
|
|
253
|
+
const keyVar = state.rerankerProvider === 'jina' ? 'JINA_API_KEY' : 'COHERE_API_KEY';
|
|
254
|
+
summaryLines.push(`Reranker: ${state.rerankerProvider} (set ${keyVar} env var)`);
|
|
355
255
|
}
|
|
356
|
-
|
|
357
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
256
|
+
return { lines: summaryLines, autoMemoryWarning };
|
|
358
257
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
258
|
+
async function onboardingCommand() {
|
|
259
|
+
const gitRoot = (0, store_1.findGitRoot)(process.cwd());
|
|
260
|
+
// Use Function constructor to bypass TypeScript's import() -> require() transform.
|
|
261
|
+
// ink, ink-text-input, ink-select-input are ESM-only packages that cannot be
|
|
262
|
+
// require()'d from a CommonJS bundle; this ensures Node uses its ESM loader.
|
|
263
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
264
|
+
const esmImport = new Function('specifier', 'return import(specifier)');
|
|
265
|
+
const reactMod = await esmImport('react');
|
|
266
|
+
const React = (reactMod.default ?? reactMod);
|
|
267
|
+
const { useState, useRef } = React;
|
|
268
|
+
const inkMod = await esmImport('ink');
|
|
269
|
+
const { render, Box, Text, useInput } = inkMod;
|
|
270
|
+
const inkTextInputMod = await esmImport('ink-text-input');
|
|
271
|
+
const TextInput = inkTextInputMod.default;
|
|
272
|
+
const inkSelectInputMod = await esmImport('ink-select-input');
|
|
273
|
+
const SelectInput = inkSelectInputMod.default;
|
|
274
|
+
const defaultName = detectProjectName();
|
|
275
|
+
const platformItems = detectPlatforms();
|
|
276
|
+
const detectedPlatforms = getDetectedPlatforms(platformItems);
|
|
277
|
+
const searchEngineItems = [
|
|
278
|
+
{ label: 'Text (recommended, zero setup)', value: 'text' },
|
|
279
|
+
{ label: 'Vector / LanceDB (better recall, requires Ollama or OpenAI)', value: 'lancedb' },
|
|
368
280
|
];
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
281
|
+
function InlineMultiSelect({ label, items, defaultSelected = [], onSubmit }) {
|
|
282
|
+
const [cursor, setCursor] = useState(0);
|
|
283
|
+
const [selected, setSelected] = useState(new Set(defaultSelected));
|
|
284
|
+
useInput((input, key) => {
|
|
285
|
+
if (key.upArrow) {
|
|
286
|
+
setCursor(c => Math.max(0, c - 1));
|
|
287
|
+
}
|
|
288
|
+
if (key.downArrow) {
|
|
289
|
+
setCursor(c => Math.min(items.length - 1, c + 1));
|
|
290
|
+
}
|
|
291
|
+
if (input === ' ') {
|
|
292
|
+
const item = items[cursor];
|
|
293
|
+
if (item && !item.disabled) {
|
|
294
|
+
setSelected(prev => {
|
|
295
|
+
const next = new Set(prev);
|
|
296
|
+
if (next.has(item.value)) {
|
|
297
|
+
next.delete(item.value);
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
next.add(item.value);
|
|
301
|
+
}
|
|
302
|
+
return next;
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (key.return) {
|
|
307
|
+
setSelected(prev => { onSubmit([...prev]); return prev; });
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
return React.createElement(Box, { flexDirection: 'column', marginBottom: 1 }, React.createElement(Text, { bold: true }, label), React.createElement(Text, { dimColor: true }, ' (↑↓ navigate · Space toggle · Enter confirm)'), ...items.map((item, i) => React.createElement(Box, { key: item.value }, React.createElement(Text, { color: (i === cursor ? 'cyan' : undefined) }, ` ${selected.has(item.value) ? '◉' : '◯'} ${item.label}`, item.hint ? React.createElement(Text, { dimColor: true }, ` ${item.hint}`) : null))));
|
|
392
311
|
}
|
|
393
|
-
(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
312
|
+
function OnboardingApp() {
|
|
313
|
+
const [state, setState] = useState({
|
|
314
|
+
step: 'project-name',
|
|
315
|
+
projectName: defaultName,
|
|
316
|
+
projectDir: '.memobank',
|
|
317
|
+
platforms: detectedPlatforms,
|
|
318
|
+
enableAutoMemory: true,
|
|
319
|
+
workspaceRemote: '',
|
|
320
|
+
searchEngine: 'text',
|
|
321
|
+
embeddingProvider: '',
|
|
322
|
+
embeddingUrl: 'http://localhost:11434',
|
|
323
|
+
embeddingModel: 'mxbai-embed-large',
|
|
324
|
+
embeddingApiKey: '',
|
|
325
|
+
enableReranker: false,
|
|
326
|
+
rerankerProvider: '',
|
|
327
|
+
});
|
|
328
|
+
const [nameInput, setNameInput] = useState(defaultName);
|
|
329
|
+
const [projectDirInput, setProjectDirInput] = useState('.memobank');
|
|
330
|
+
const [workspaceInput, setWorkspaceInput] = useState('');
|
|
331
|
+
const [ollamaUrlInput, setOllamaUrlInput] = useState('http://localhost:11434');
|
|
332
|
+
const [ollamaModelInput, setOllamaModelInput] = useState('mxbai-embed-large');
|
|
333
|
+
const [openaiKeyInput, setOpenaiKeyInput] = useState('');
|
|
334
|
+
const [jinaKeyInput, setJinaKeyInput] = useState('');
|
|
335
|
+
const [done, setDone] = useState(false);
|
|
336
|
+
const [summary, setSummary] = useState([]);
|
|
337
|
+
const [autoMemoryWarning, setAutoMemoryWarning] = useState(false);
|
|
338
|
+
// Prevent double-submission
|
|
339
|
+
const setupRunning = useRef(false);
|
|
340
|
+
if (done) {
|
|
341
|
+
return React.createElement(Box, { flexDirection: 'column', marginTop: 1 }, React.createElement(Text, { color: 'green', bold: true }, '✓ memobank initialized!'), ...summary.map((line, i) => React.createElement(Text, { key: i, dimColor: true }, ` ${line}`)), React.createElement(Text, { dimColor: true }, 'Run: memo recall "anything" to test'), autoMemoryWarning
|
|
342
|
+
? React.createElement(Box, { flexDirection: 'column', marginTop: 1 }, React.createElement(Text, { color: 'yellow', bold: true }, '⚠ Auto-memory is off'), React.createElement(Text, { color: 'yellow' }, ' Claude Code won\'t read or write project memories in .memobank/'), React.createElement(Text, { dimColor: true }, ' To enable later: set "autoMemoryEnabled": true in ~/.claude/settings.json'))
|
|
343
|
+
: null);
|
|
344
|
+
}
|
|
345
|
+
return React.createElement(Box, { flexDirection: 'column', padding: 1 }, React.createElement(Text, { bold: true, color: 'cyan' }, '🧠 Memobank Setup'), React.createElement(Text, null, ' '), state.step === 'project-name' ? React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, null, 'Project name:'), React.createElement(TextInput, {
|
|
346
|
+
value: nameInput,
|
|
347
|
+
onChange: setNameInput,
|
|
348
|
+
onSubmit: (value) => {
|
|
349
|
+
setState(s => ({ ...s, step: 'project-dir', projectName: value || defaultName }));
|
|
350
|
+
},
|
|
351
|
+
})) : null, state.step === 'project-dir' ? React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, { bold: true }, 'Project memory directory'), React.createElement(Text, { dimColor: true }, ` Folder inside ${gitRoot}/ that stores your project memories`), React.createElement(Text, { dimColor: true }, ' Default is .memobank — press Enter to confirm, or type a custom name:'), React.createElement(TextInput, {
|
|
352
|
+
value: projectDirInput,
|
|
353
|
+
onChange: setProjectDirInput,
|
|
354
|
+
onSubmit: (value) => {
|
|
355
|
+
const dir = (value || '.memobank').replace(/^\/+|\/+$/g, '');
|
|
356
|
+
setState(s => ({ ...s, step: 'platforms', projectDir: dir }));
|
|
357
|
+
},
|
|
358
|
+
})) : null, state.step === 'platforms' ? React.createElement(InlineMultiSelect, {
|
|
359
|
+
label: 'Select platforms to integrate:',
|
|
360
|
+
items: platformItems,
|
|
361
|
+
defaultSelected: detectedPlatforms,
|
|
362
|
+
onSubmit: (selected) => {
|
|
363
|
+
const needsAutoMemoryCheck = selected.includes('claude-code') && isAutoMemoryDisabled();
|
|
364
|
+
setState(s => ({
|
|
365
|
+
...s,
|
|
366
|
+
platforms: selected,
|
|
367
|
+
step: needsAutoMemoryCheck ? 'auto-memory-check' : 'workspace-remote',
|
|
368
|
+
}));
|
|
369
|
+
},
|
|
370
|
+
}) : null, state.step === 'auto-memory-check' ? React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, { bold: true, color: 'yellow' }, '⚠ Claude Code auto-memory is disabled'), React.createElement(Text, null, ' '), React.createElement(Text, null, 'memobank stores project memories in .memobank/ and relies on Claude Code\'s'), React.createElement(Text, null, 'auto-memory to load them at session start and save new ones automatically.'), React.createElement(Text, null, 'With auto-memory off, Claude Code won\'t read or write to .memobank/.'), React.createElement(Text, null, ' '), React.createElement(Text, { bold: true }, 'Enable auto-memory for this project?'), React.createElement(SelectInput, {
|
|
371
|
+
items: [
|
|
372
|
+
{ label: 'Yes — enable auto-memory (recommended)', value: 'yes' },
|
|
373
|
+
{ label: 'No — keep it off', value: 'no' },
|
|
374
|
+
],
|
|
375
|
+
onSelect: (item) => {
|
|
376
|
+
const enable = String(item.value) === 'yes';
|
|
377
|
+
setState(s => ({ ...s, enableAutoMemory: enable, step: 'workspace-remote' }));
|
|
378
|
+
},
|
|
379
|
+
})) : null, state.step === 'workspace-remote' ? React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, { bold: true }, 'Workspace remote'), React.createElement(Text, { dimColor: true }, ' Org-wide memories shared across repos (e.g. git@github.com:myorg/platform-docs.git)'), React.createElement(Text, { dimColor: true }, ' Optional — press Enter to skip:'), React.createElement(TextInput, {
|
|
380
|
+
value: workspaceInput,
|
|
381
|
+
onChange: setWorkspaceInput,
|
|
382
|
+
onSubmit: (value) => {
|
|
383
|
+
setState(s => ({ ...s, step: 'search-engine', workspaceRemote: value }));
|
|
384
|
+
},
|
|
385
|
+
})) : null, state.step === 'search-engine' ? React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, { bold: true }, 'Search engine:'), React.createElement(SelectInput, {
|
|
386
|
+
items: searchEngineItems,
|
|
387
|
+
onSelect: (item) => {
|
|
388
|
+
const engine = String(item.value);
|
|
389
|
+
if (engine === 'lancedb') {
|
|
390
|
+
setState(s => ({ ...s, step: 'embedding-provider', searchEngine: engine }));
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
setState(s => ({ ...s, step: 'reranker', searchEngine: engine }));
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
})) : null, state.step === 'embedding-provider' ? React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, { bold: true }, 'Embedding provider:'), React.createElement(SelectInput, {
|
|
397
|
+
items: [
|
|
398
|
+
{ label: 'Ollama (local, no API key needed)', value: 'ollama' },
|
|
399
|
+
{ label: 'OpenAI (cloud, requires API key)', value: 'openai' },
|
|
400
|
+
{ label: 'Jina AI (cloud, requires API key)', value: 'jina' },
|
|
401
|
+
],
|
|
402
|
+
onSelect: (item) => {
|
|
403
|
+
const provider = String(item.value);
|
|
404
|
+
if (provider === 'ollama') {
|
|
405
|
+
setState(s => ({ ...s, step: 'ollama-url', embeddingProvider: provider }));
|
|
406
|
+
}
|
|
407
|
+
else if (provider === 'openai') {
|
|
408
|
+
setState(s => ({ ...s, step: 'openai-key', embeddingProvider: provider }));
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
setState(s => ({ ...s, step: 'jina-key', embeddingProvider: provider }));
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
})) : null, state.step === 'ollama-url' ? React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, null, 'Ollama base URL:'), React.createElement(Text, { dimColor: true }, ' (default: http://localhost:11434 — press Enter to confirm)'), React.createElement(TextInput, {
|
|
415
|
+
value: ollamaUrlInput,
|
|
416
|
+
onChange: setOllamaUrlInput,
|
|
417
|
+
onSubmit: (value) => {
|
|
418
|
+
setState(s => ({ ...s, step: 'ollama-model', embeddingUrl: value || 'http://localhost:11434' }));
|
|
419
|
+
},
|
|
420
|
+
})) : null, state.step === 'ollama-model' ? React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, null, 'Ollama embedding model:'), React.createElement(Text, { dimColor: true }, ' (default: mxbai-embed-large — run `ollama pull mxbai-embed-large` to install)'), React.createElement(TextInput, {
|
|
421
|
+
value: ollamaModelInput,
|
|
422
|
+
onChange: setOllamaModelInput,
|
|
423
|
+
onSubmit: (value) => {
|
|
424
|
+
setState(s => ({ ...s, step: 'reranker', embeddingModel: value || 'mxbai-embed-large' }));
|
|
425
|
+
},
|
|
426
|
+
})) : null, state.step === 'openai-key' ? React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, null, 'OpenAI API key:'), React.createElement(Text, { dimColor: true }, ' (will be saved to .env — press Enter to skip and set OPENAI_API_KEY manually)'), React.createElement(TextInput, {
|
|
427
|
+
value: openaiKeyInput,
|
|
428
|
+
onChange: setOpenaiKeyInput,
|
|
429
|
+
onSubmit: (value) => {
|
|
430
|
+
setState(s => ({ ...s, step: 'reranker', embeddingApiKey: value }));
|
|
431
|
+
},
|
|
432
|
+
})) : null, state.step === 'jina-key' ? React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, null, 'Jina API key:'), React.createElement(Text, { dimColor: true }, ' (will be saved to .env — press Enter to skip and set JINA_API_KEY manually)'), React.createElement(TextInput, {
|
|
433
|
+
value: jinaKeyInput,
|
|
434
|
+
onChange: setJinaKeyInput,
|
|
435
|
+
onSubmit: (value) => {
|
|
436
|
+
setState(s => ({ ...s, step: 'reranker', embeddingApiKey: value }));
|
|
437
|
+
},
|
|
438
|
+
})) : null, state.step === 'reranker' ? React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, { bold: true }, 'Enable reranker?'), React.createElement(Text, { dimColor: true }, ' Re-ranks results with AI for better precision (needs Jina or Cohere API key)'), React.createElement(SelectInput, {
|
|
439
|
+
items: [
|
|
440
|
+
{ label: 'No', value: 'no' },
|
|
441
|
+
{ label: 'Yes', value: 'yes' },
|
|
442
|
+
],
|
|
443
|
+
onSelect: (item) => {
|
|
444
|
+
if (String(item.value) === 'yes') {
|
|
445
|
+
setState(s => ({ ...s, step: 'reranker-provider' }));
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
if (setupRunning.current)
|
|
449
|
+
return;
|
|
450
|
+
setupRunning.current = true;
|
|
451
|
+
const finalState = { ...state, step: 'done', enableReranker: false };
|
|
452
|
+
setState(finalState);
|
|
453
|
+
runSetup(finalState, gitRoot).then(({ lines, autoMemoryWarning: warn }) => {
|
|
454
|
+
setSummary(lines);
|
|
455
|
+
setAutoMemoryWarning(warn);
|
|
456
|
+
setDone(true);
|
|
457
|
+
}).catch((err) => {
|
|
458
|
+
setSummary([`Setup failed: ${err.message}`]);
|
|
459
|
+
setDone(true);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
})) : null, state.step === 'reranker-provider' ? React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, { bold: true }, 'Reranker provider:'), React.createElement(SelectInput, {
|
|
464
|
+
items: [
|
|
465
|
+
{ label: 'Jina AI (set JINA_API_KEY)', value: 'jina' },
|
|
466
|
+
{ label: 'Cohere (set COHERE_API_KEY)', value: 'cohere' },
|
|
467
|
+
],
|
|
468
|
+
onSelect: (item) => {
|
|
469
|
+
if (setupRunning.current)
|
|
470
|
+
return;
|
|
471
|
+
setupRunning.current = true;
|
|
472
|
+
const finalState = { ...state, step: 'done', enableReranker: true, rerankerProvider: String(item.value) };
|
|
473
|
+
setState(finalState);
|
|
474
|
+
runSetup(finalState, gitRoot).then(({ lines, autoMemoryWarning: warn }) => {
|
|
475
|
+
setSummary(lines);
|
|
476
|
+
setAutoMemoryWarning(warn);
|
|
477
|
+
setDone(true);
|
|
478
|
+
}).catch((err) => {
|
|
479
|
+
setSummary([`Setup failed: ${err.message}`]);
|
|
480
|
+
setDone(true);
|
|
481
|
+
});
|
|
482
|
+
},
|
|
483
|
+
})) : null);
|
|
411
484
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
console.log('Next steps:\n');
|
|
415
|
-
console.log(' \x1B[36mmemo write lesson\x1B[39m Create your first memory');
|
|
416
|
-
console.log(' \x1B[36mmemo recall "query"\x1B[39m Search memories');
|
|
417
|
-
console.log(' \x1B[36mmemo lifecycle report\x1B[39m View memory statistics');
|
|
418
|
-
console.log(' \x1B[36mmemo --help\x1B[39m See all commands\n');
|
|
485
|
+
const { waitUntilExit } = render(React.createElement(OnboardingApp));
|
|
486
|
+
await waitUntilExit();
|
|
419
487
|
}
|
|
420
488
|
//# sourceMappingURL=onboarding.js.map
|