@getlore/cli 0.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 (148) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +80 -0
  3. package/dist/cli/colors.d.ts +48 -0
  4. package/dist/cli/colors.js +48 -0
  5. package/dist/cli/commands/ask.d.ts +7 -0
  6. package/dist/cli/commands/ask.js +97 -0
  7. package/dist/cli/commands/auth.d.ts +10 -0
  8. package/dist/cli/commands/auth.js +484 -0
  9. package/dist/cli/commands/daemon.d.ts +22 -0
  10. package/dist/cli/commands/daemon.js +244 -0
  11. package/dist/cli/commands/docs.d.ts +7 -0
  12. package/dist/cli/commands/docs.js +188 -0
  13. package/dist/cli/commands/extensions.d.ts +7 -0
  14. package/dist/cli/commands/extensions.js +204 -0
  15. package/dist/cli/commands/misc.d.ts +7 -0
  16. package/dist/cli/commands/misc.js +172 -0
  17. package/dist/cli/commands/pending.d.ts +7 -0
  18. package/dist/cli/commands/pending.js +63 -0
  19. package/dist/cli/commands/projects.d.ts +7 -0
  20. package/dist/cli/commands/projects.js +136 -0
  21. package/dist/cli/commands/search.d.ts +7 -0
  22. package/dist/cli/commands/search.js +102 -0
  23. package/dist/cli/commands/skills.d.ts +24 -0
  24. package/dist/cli/commands/skills.js +447 -0
  25. package/dist/cli/commands/sources.d.ts +7 -0
  26. package/dist/cli/commands/sources.js +121 -0
  27. package/dist/cli/commands/sync.d.ts +31 -0
  28. package/dist/cli/commands/sync.js +768 -0
  29. package/dist/cli/helpers.d.ts +30 -0
  30. package/dist/cli/helpers.js +119 -0
  31. package/dist/core/auth.d.ts +62 -0
  32. package/dist/core/auth.js +330 -0
  33. package/dist/core/config.d.ts +41 -0
  34. package/dist/core/config.js +96 -0
  35. package/dist/core/data-repo.d.ts +31 -0
  36. package/dist/core/data-repo.js +146 -0
  37. package/dist/core/embedder.d.ts +22 -0
  38. package/dist/core/embedder.js +104 -0
  39. package/dist/core/git.d.ts +37 -0
  40. package/dist/core/git.js +140 -0
  41. package/dist/core/index.d.ts +4 -0
  42. package/dist/core/index.js +5 -0
  43. package/dist/core/insight-extractor.d.ts +26 -0
  44. package/dist/core/insight-extractor.js +114 -0
  45. package/dist/core/local-search.d.ts +43 -0
  46. package/dist/core/local-search.js +221 -0
  47. package/dist/core/themes.d.ts +15 -0
  48. package/dist/core/themes.js +77 -0
  49. package/dist/core/types.d.ts +177 -0
  50. package/dist/core/types.js +9 -0
  51. package/dist/core/user-settings.d.ts +15 -0
  52. package/dist/core/user-settings.js +42 -0
  53. package/dist/core/vector-store-lance.d.ts +98 -0
  54. package/dist/core/vector-store-lance.js +384 -0
  55. package/dist/core/vector-store-supabase.d.ts +89 -0
  56. package/dist/core/vector-store-supabase.js +295 -0
  57. package/dist/core/vector-store.d.ts +131 -0
  58. package/dist/core/vector-store.js +503 -0
  59. package/dist/daemon-runner.d.ts +8 -0
  60. package/dist/daemon-runner.js +246 -0
  61. package/dist/extensions/config.d.ts +22 -0
  62. package/dist/extensions/config.js +102 -0
  63. package/dist/extensions/proposals.d.ts +30 -0
  64. package/dist/extensions/proposals.js +178 -0
  65. package/dist/extensions/registry.d.ts +35 -0
  66. package/dist/extensions/registry.js +309 -0
  67. package/dist/extensions/sandbox.d.ts +16 -0
  68. package/dist/extensions/sandbox.js +17 -0
  69. package/dist/extensions/types.d.ts +114 -0
  70. package/dist/extensions/types.js +4 -0
  71. package/dist/extensions/worker.d.ts +1 -0
  72. package/dist/extensions/worker.js +49 -0
  73. package/dist/index.d.ts +17 -0
  74. package/dist/index.js +105 -0
  75. package/dist/mcp/handlers/archive-project.d.ts +51 -0
  76. package/dist/mcp/handlers/archive-project.js +112 -0
  77. package/dist/mcp/handlers/get-quotes.d.ts +27 -0
  78. package/dist/mcp/handlers/get-quotes.js +61 -0
  79. package/dist/mcp/handlers/get-source.d.ts +9 -0
  80. package/dist/mcp/handlers/get-source.js +40 -0
  81. package/dist/mcp/handlers/ingest.d.ts +25 -0
  82. package/dist/mcp/handlers/ingest.js +305 -0
  83. package/dist/mcp/handlers/list-projects.d.ts +4 -0
  84. package/dist/mcp/handlers/list-projects.js +16 -0
  85. package/dist/mcp/handlers/list-sources.d.ts +11 -0
  86. package/dist/mcp/handlers/list-sources.js +20 -0
  87. package/dist/mcp/handlers/research-agent.d.ts +21 -0
  88. package/dist/mcp/handlers/research-agent.js +369 -0
  89. package/dist/mcp/handlers/research.d.ts +22 -0
  90. package/dist/mcp/handlers/research.js +225 -0
  91. package/dist/mcp/handlers/retain.d.ts +18 -0
  92. package/dist/mcp/handlers/retain.js +92 -0
  93. package/dist/mcp/handlers/search.d.ts +52 -0
  94. package/dist/mcp/handlers/search.js +145 -0
  95. package/dist/mcp/handlers/sync.d.ts +47 -0
  96. package/dist/mcp/handlers/sync.js +211 -0
  97. package/dist/mcp/server.d.ts +10 -0
  98. package/dist/mcp/server.js +268 -0
  99. package/dist/mcp/tools.d.ts +16 -0
  100. package/dist/mcp/tools.js +297 -0
  101. package/dist/sync/config.d.ts +26 -0
  102. package/dist/sync/config.js +140 -0
  103. package/dist/sync/discover.d.ts +51 -0
  104. package/dist/sync/discover.js +190 -0
  105. package/dist/sync/index.d.ts +11 -0
  106. package/dist/sync/index.js +11 -0
  107. package/dist/sync/process.d.ts +50 -0
  108. package/dist/sync/process.js +285 -0
  109. package/dist/sync/processors.d.ts +24 -0
  110. package/dist/sync/processors.js +351 -0
  111. package/dist/tui/browse-handlers-ask.d.ts +30 -0
  112. package/dist/tui/browse-handlers-ask.js +372 -0
  113. package/dist/tui/browse-handlers-autocomplete.d.ts +49 -0
  114. package/dist/tui/browse-handlers-autocomplete.js +270 -0
  115. package/dist/tui/browse-handlers-extensions.d.ts +18 -0
  116. package/dist/tui/browse-handlers-extensions.js +107 -0
  117. package/dist/tui/browse-handlers-pending.d.ts +22 -0
  118. package/dist/tui/browse-handlers-pending.js +100 -0
  119. package/dist/tui/browse-handlers-research.d.ts +32 -0
  120. package/dist/tui/browse-handlers-research.js +363 -0
  121. package/dist/tui/browse-handlers-tools.d.ts +42 -0
  122. package/dist/tui/browse-handlers-tools.js +289 -0
  123. package/dist/tui/browse-handlers.d.ts +239 -0
  124. package/dist/tui/browse-handlers.js +1944 -0
  125. package/dist/tui/browse-render-extensions.d.ts +14 -0
  126. package/dist/tui/browse-render-extensions.js +114 -0
  127. package/dist/tui/browse-render-tools.d.ts +18 -0
  128. package/dist/tui/browse-render-tools.js +259 -0
  129. package/dist/tui/browse-render.d.ts +51 -0
  130. package/dist/tui/browse-render.js +599 -0
  131. package/dist/tui/browse-types.d.ts +142 -0
  132. package/dist/tui/browse-types.js +70 -0
  133. package/dist/tui/browse-ui.d.ts +10 -0
  134. package/dist/tui/browse-ui.js +432 -0
  135. package/dist/tui/browse.d.ts +17 -0
  136. package/dist/tui/browse.js +625 -0
  137. package/dist/tui/markdown.d.ts +22 -0
  138. package/dist/tui/markdown.js +223 -0
  139. package/package.json +71 -0
  140. package/plugins/claude-code/.claude-plugin/plugin.json +10 -0
  141. package/plugins/claude-code/.mcp.json +6 -0
  142. package/plugins/claude-code/skills/lore/SKILL.md +63 -0
  143. package/plugins/codex/SKILL.md +36 -0
  144. package/plugins/codex/agents/openai.yaml +10 -0
  145. package/plugins/gemini/GEMINI.md +31 -0
  146. package/plugins/gemini/gemini-extension.json +11 -0
  147. package/skills/generic-agent.md +99 -0
  148. package/skills/openclaw.md +67 -0
@@ -0,0 +1,484 @@
1
+ /**
2
+ * Auth Commands
3
+ *
4
+ * lore auth login — interactive OTP flow
5
+ * lore auth logout — clear session
6
+ * lore auth whoami — show current user/status
7
+ * lore setup — guided wizard (config + login + init + first sync source)
8
+ */
9
+ import path from 'path';
10
+ import { c } from '../colors.js';
11
+ // ============================================================================
12
+ // Readline helper
13
+ // ============================================================================
14
+ async function prompt(question, defaultValue) {
15
+ const readline = await import('readline');
16
+ const rl = readline.createInterface({
17
+ input: process.stdin,
18
+ output: process.stdout,
19
+ });
20
+ return new Promise((resolve) => {
21
+ const display = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
22
+ rl.question(display, (answer) => {
23
+ rl.close();
24
+ resolve(answer.trim() || defaultValue || '');
25
+ });
26
+ });
27
+ }
28
+ // ============================================================================
29
+ // Command Registration
30
+ // ============================================================================
31
+ export function registerAuthCommands(program) {
32
+ const authCmd = program
33
+ .command('auth')
34
+ .description('Authentication commands (login, logout, whoami)');
35
+ // ── lore auth login ──────────────────────────────────────────────────────
36
+ authCmd
37
+ .command('login')
38
+ .description('Sign in with email (OTP)')
39
+ .option('-e, --email <email>', 'Email address')
40
+ .action(async (options) => {
41
+ const { sendOTP, verifyOTP, sessionFromMagicLink, waitForMagicLinkCallback, isAuthenticated } = await import('../../core/auth.js');
42
+ // Check if already logged in
43
+ if (await isAuthenticated()) {
44
+ const { loadAuthSession } = await import('../../core/auth.js');
45
+ const session = await loadAuthSession();
46
+ console.log(c.success(`Already logged in as ${session?.user.email}`));
47
+ console.log(c.dim('Run \'lore logout\' first to switch accounts.'));
48
+ return;
49
+ }
50
+ const email = options.email || await prompt('Email');
51
+ if (!email) {
52
+ console.error(c.error('Email is required'));
53
+ process.exit(1);
54
+ }
55
+ // Start the localhost callback server before sending the OTP
56
+ // so it's ready when the user clicks the magic link
57
+ const callback = waitForMagicLinkCallback({
58
+ onListening: () => {
59
+ // Server is ready — now send the OTP
60
+ },
61
+ });
62
+ console.log(c.dim(`Sending code to ${email}...`));
63
+ const { error: sendError } = await sendOTP(email);
64
+ if (sendError) {
65
+ callback.abort();
66
+ console.error(c.error(`Failed to send code: ${sendError}`));
67
+ process.exit(1);
68
+ }
69
+ console.log(c.success('Check your email!'));
70
+ console.log(c.dim('Click the magic link in the email — it will sign you in automatically.'));
71
+ console.log(c.dim('Or paste a 6-digit code if your email shows one.'));
72
+ console.log('');
73
+ console.log(c.dim('Waiting for magic link click...'));
74
+ // Race: callback server catches the magic link, or user pastes a code/URL
75
+ const readline = await import('readline');
76
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
77
+ const manualInput = new Promise((resolve) => {
78
+ rl.question('Or paste code/URL here: ', (answer) => resolve(answer.trim()));
79
+ });
80
+ const result = await Promise.race([
81
+ callback.promise.then(session => ({ type: 'callback', session })),
82
+ manualInput.then(input => ({ type: 'manual', input })),
83
+ ]);
84
+ // Clear the readline prompt line before printing result
85
+ rl.close();
86
+ process.stdout.write('\x1b[1A\x1b[2K'); // Move up one line and clear it
87
+ let session;
88
+ let verifyError;
89
+ if (result.type === 'callback' && result.session) {
90
+ session = result.session;
91
+ }
92
+ else if (result.type === 'manual' && result.input) {
93
+ callback.abort();
94
+ const input = result.input;
95
+ if (input.startsWith('http')) {
96
+ const r = await sessionFromMagicLink(input);
97
+ session = r.session;
98
+ verifyError = r.error;
99
+ }
100
+ else {
101
+ const r = await verifyOTP(email, input);
102
+ session = r.session;
103
+ verifyError = r.error;
104
+ }
105
+ }
106
+ else {
107
+ callback.abort();
108
+ console.error(c.error('Login timed out. Try again with \'lore login\'.'));
109
+ process.exit(1);
110
+ }
111
+ if (verifyError || !session) {
112
+ console.error(c.error(`Verification failed: ${verifyError || 'Unknown error'}`));
113
+ process.exit(1);
114
+ }
115
+ console.log(c.success(`Logged in as ${session.user.email}`));
116
+ });
117
+ // ── lore auth logout ─────────────────────────────────────────────────────
118
+ authCmd
119
+ .command('logout')
120
+ .description('Sign out and clear session')
121
+ .action(async () => {
122
+ const { clearAuthSession } = await import('../../core/auth.js');
123
+ const { resetDatabaseConnection } = await import('../../core/vector-store.js');
124
+ await clearAuthSession();
125
+ resetDatabaseConnection();
126
+ console.log(c.success('Logged out.'));
127
+ });
128
+ // ── lore auth whoami ─────────────────────────────────────────────────────
129
+ authCmd
130
+ .command('whoami')
131
+ .description('Show current authentication status')
132
+ .action(async () => {
133
+ const { getValidSession } = await import('../../core/auth.js');
134
+ // Check for service key mode
135
+ if (process.env.SUPABASE_SERVICE_KEY) {
136
+ console.log(c.warning('Service key mode (SUPABASE_SERVICE_KEY set)'));
137
+ console.log(c.dim('RLS is bypassed. All data is accessible.'));
138
+ return;
139
+ }
140
+ const session = await getValidSession();
141
+ if (!session) {
142
+ console.log(c.dim('Not logged in. Run \'lore login\' to sign in.'));
143
+ return;
144
+ }
145
+ console.log(`${c.bold('Email:')} ${session.user.email}`);
146
+ console.log(`${c.bold('User ID:')} ${c.dim(session.user.id)}`);
147
+ const expiresAt = new Date(session.expires_at * 1000);
148
+ const now = new Date();
149
+ if (expiresAt > now) {
150
+ const minutes = Math.round((expiresAt.getTime() - now.getTime()) / 60000);
151
+ console.log(`${c.bold('Session:')} ${c.success('active')} (expires in ${minutes}m)`);
152
+ }
153
+ else {
154
+ console.log(`${c.bold('Session:')} ${c.warning('expired, will refresh on next use')}`);
155
+ }
156
+ });
157
+ // ── lore setup ──────────────────────────────────────────────────────────
158
+ program
159
+ .command('setup')
160
+ .description('Guided setup wizard (config, login, init)')
161
+ .action(async () => {
162
+ const { saveLoreConfig, getLoreConfigPath } = await import('../../core/config.js');
163
+ const { sendOTP, verifyOTP, sessionFromMagicLink, isAuthenticated, loadAuthSession } = await import('../../core/auth.js');
164
+ const { bridgeConfigToEnv } = await import('../../core/config.js');
165
+ console.log(`\n${c.title('Lore Setup Wizard')}`);
166
+ console.log(`${c.dim('=')}`.repeat(40) + '\n');
167
+ // ── Step 1: Configuration ───────────────────────────────────────
168
+ console.log(c.bold('Step 1: Configuration\n'));
169
+ const { expandPath } = await import('../../sync/config.js');
170
+ const defaultDataDir = process.env.LORE_DATA_DIR || '~/.lore';
171
+ const dataDir = await prompt('Data directory', defaultDataDir);
172
+ const openaiApiKey = await prompt('OpenAI API Key (for embeddings)');
173
+ const anthropicApiKey = await prompt('Anthropic API Key (for sync & research)');
174
+ await saveLoreConfig({
175
+ ...(dataDir ? { data_dir: expandPath(dataDir) } : {}),
176
+ ...(openaiApiKey ? { openai_api_key: openaiApiKey } : {}),
177
+ ...(anthropicApiKey ? { anthropic_api_key: anthropicApiKey } : {}),
178
+ });
179
+ console.log(c.success(`Config saved to ${getLoreConfigPath()}\n`));
180
+ // Bridge new config values into process.env for subsequent steps
181
+ await bridgeConfigToEnv();
182
+ // ── Step 2: Login ──────────────────────────────────────────────────
183
+ console.log(c.bold('Step 2: Login\n'));
184
+ if (await isAuthenticated()) {
185
+ const session = await loadAuthSession();
186
+ console.log(c.success(`Already logged in as ${session?.user.email}\n`));
187
+ }
188
+ else {
189
+ const email = await prompt('Email');
190
+ if (!email) {
191
+ console.error(c.error('Email is required'));
192
+ process.exit(1);
193
+ }
194
+ console.log(c.dim(`Sending code to ${email}...`));
195
+ const { error: sendError } = await sendOTP(email);
196
+ if (sendError) {
197
+ console.error(c.error(`Failed to send code: ${sendError}`));
198
+ process.exit(1);
199
+ }
200
+ console.log(c.success('Check your email!'));
201
+ console.log(c.dim('You may receive a 6-digit code or a magic link URL.'));
202
+ const response = await prompt('Paste the code or the full magic link URL');
203
+ if (!response) {
204
+ console.error(c.error('Code or magic link is required'));
205
+ process.exit(1);
206
+ }
207
+ let session;
208
+ let verifyError;
209
+ if (response.startsWith('http')) {
210
+ const result = await sessionFromMagicLink(response);
211
+ session = result.session;
212
+ verifyError = result.error;
213
+ }
214
+ else {
215
+ const result = await verifyOTP(email, response);
216
+ session = result.session;
217
+ verifyError = result.error;
218
+ }
219
+ if (verifyError || !session) {
220
+ console.error(c.error(`Verification failed: ${verifyError || 'Unknown error'}`));
221
+ process.exit(1);
222
+ }
223
+ console.log(c.success(`Logged in as ${session.user.email}\n`));
224
+ }
225
+ // ── Step 3: Data Repository ────────────────────────────────────────
226
+ console.log(c.bold('Step 3: Data Repository\n'));
227
+ const { existsSync } = await import('fs');
228
+ const { initDataRepo, isGhAvailable, createGithubRepo, getGitRemoteUrl, isGitRepo } = await import('../../core/data-repo.js');
229
+ const { getUserSetting, setUserSetting, SETTING_DATA_REPO_URL } = await import('../../core/user-settings.js');
230
+ const { resetDatabaseConnection } = await import('../../core/vector-store.js');
231
+ // Reset Supabase client so it picks up the fresh auth session from Step 2
232
+ resetDatabaseConnection();
233
+ const resolvedDataDir = expandPath(dataDir || '~/.lore');
234
+ const dirExists = existsSync(resolvedDataDir);
235
+ const hasGitRemote = dirExists && isGitRepo(resolvedDataDir) && !!getGitRemoteUrl(resolvedDataDir);
236
+ if (dirExists && hasGitRemote) {
237
+ // Already fully set up
238
+ const remoteUrl = getGitRemoteUrl(resolvedDataDir);
239
+ console.log(c.success(`Data repo already set up at ${resolvedDataDir}`));
240
+ console.log(c.dim(`Remote: ${remoteUrl}\n`));
241
+ // Save URL to Supabase if not already saved
242
+ try {
243
+ const savedUrl = await getUserSetting(SETTING_DATA_REPO_URL);
244
+ if (!savedUrl) {
245
+ await setUserSetting(SETTING_DATA_REPO_URL, remoteUrl);
246
+ console.log(c.dim('Saved repo URL to your account for cross-machine discovery.\n'));
247
+ }
248
+ }
249
+ catch (err) {
250
+ console.log(c.dim(`Note: Could not sync repo URL to account: ${err instanceof Error ? err.message : err}`));
251
+ }
252
+ }
253
+ else if (!dirExists) {
254
+ // Check Supabase for saved repo URL (Machine B scenario)
255
+ let savedUrl = null;
256
+ try {
257
+ savedUrl = await getUserSetting(SETTING_DATA_REPO_URL);
258
+ }
259
+ catch (err) {
260
+ console.log(c.dim(`Could not check for existing repo URL: ${err instanceof Error ? err.message : err}`));
261
+ }
262
+ if (savedUrl) {
263
+ // Machine B: clone existing repo
264
+ console.log(c.success(`Found your data repo URL: ${savedUrl}`));
265
+ const cloneIt = await prompt('Clone it to ' + resolvedDataDir + '? (y/n)', 'y');
266
+ if (cloneIt.toLowerCase() === 'y') {
267
+ try {
268
+ const { execSync } = await import('child_process');
269
+ execSync(`git clone ${savedUrl} ${resolvedDataDir}`, { stdio: 'inherit' });
270
+ console.log(c.success('Cloned successfully!\n'));
271
+ }
272
+ catch {
273
+ console.log(c.warning('Clone failed. You can clone manually later.\n'));
274
+ }
275
+ }
276
+ else {
277
+ console.log(c.dim('Skipped. You can clone manually later.\n'));
278
+ }
279
+ }
280
+ else {
281
+ // Machine A: create fresh repo
282
+ console.log(c.dim(`Creating data repository at ${resolvedDataDir}...\n`));
283
+ await initDataRepo(resolvedDataDir);
284
+ console.log(c.success('Created data repository.\n'));
285
+ // Try to set up GitHub remote
286
+ if (await isGhAvailable()) {
287
+ const createRepo = await prompt('Create a private GitHub repo for cross-machine sync? (y/n)', 'y');
288
+ if (createRepo.toLowerCase() === 'y') {
289
+ const repoName = await prompt('Repository name', 'lore-data');
290
+ const url = await createGithubRepo(resolvedDataDir, repoName);
291
+ if (url) {
292
+ console.log(c.success(`Created and pushed to ${url}\n`));
293
+ try {
294
+ await setUserSetting(SETTING_DATA_REPO_URL, url);
295
+ console.log(c.dim('Saved repo URL for cross-machine discovery.\n'));
296
+ }
297
+ catch {
298
+ // Non-fatal
299
+ }
300
+ }
301
+ else {
302
+ console.log(c.warning('GitHub repo creation failed. You can set up a remote manually.\n'));
303
+ }
304
+ }
305
+ }
306
+ else {
307
+ const remoteUrl = await prompt('Git remote URL for cross-machine sync (or press Enter to skip)');
308
+ if (remoteUrl) {
309
+ try {
310
+ const { execSync } = await import('child_process');
311
+ execSync(`git remote add origin ${remoteUrl}`, { cwd: resolvedDataDir, stdio: 'pipe' });
312
+ execSync('git push -u origin main', { cwd: resolvedDataDir, stdio: 'pipe' });
313
+ console.log(c.success(`Remote added and pushed.\n`));
314
+ try {
315
+ await setUserSetting(SETTING_DATA_REPO_URL, remoteUrl);
316
+ }
317
+ catch {
318
+ // Non-fatal
319
+ }
320
+ }
321
+ catch {
322
+ console.log(c.warning('Could not push to remote. You can push manually later.\n'));
323
+ }
324
+ }
325
+ else {
326
+ console.log(c.dim('Skipped remote setup. You can add one later with:\n'));
327
+ console.log(c.dim(` cd ${resolvedDataDir} && git remote add origin <url> && git push -u origin main\n`));
328
+ }
329
+ }
330
+ }
331
+ }
332
+ else {
333
+ // Dir exists but no git remote — check Supabase first, then offer setup
334
+ console.log(c.dim(`Data directory exists at ${resolvedDataDir}, ensuring git is set up...\n`));
335
+ await initDataRepo(resolvedDataDir);
336
+ // Check Supabase for a saved repo URL before offering to create a new one
337
+ let savedUrl = null;
338
+ try {
339
+ savedUrl = await getUserSetting(SETTING_DATA_REPO_URL);
340
+ }
341
+ catch (err) {
342
+ console.log(c.dim(`Could not check for existing repo URL: ${err instanceof Error ? err.message : err}`));
343
+ }
344
+ if (savedUrl) {
345
+ // Found existing repo — add as remote and pull
346
+ console.log(c.success(`Found your data repo: ${savedUrl}`));
347
+ const useIt = await prompt('Add as remote and pull? (y/n)', 'y');
348
+ if (useIt.toLowerCase() === 'y') {
349
+ try {
350
+ const { execSync } = await import('child_process');
351
+ execSync(`git remote add origin ${savedUrl}`, { cwd: resolvedDataDir, stdio: 'pipe' });
352
+ execSync('git fetch origin', { cwd: resolvedDataDir, stdio: 'pipe' });
353
+ execSync('git reset --hard origin/main', { cwd: resolvedDataDir, stdio: 'pipe' });
354
+ console.log(c.success('Synced with existing repo!\n'));
355
+ }
356
+ catch {
357
+ console.log(c.warning('Could not sync. You may need to set up the remote manually.\n'));
358
+ }
359
+ }
360
+ }
361
+ else if (await isGhAvailable()) {
362
+ const createRepo = await prompt('Create a private GitHub repo for cross-machine sync? (y/n)', 'y');
363
+ if (createRepo.toLowerCase() === 'y') {
364
+ const repoName = await prompt('Repository name', 'lore-data');
365
+ const url = await createGithubRepo(resolvedDataDir, repoName);
366
+ if (url) {
367
+ console.log(c.success(`Created and pushed to ${url}\n`));
368
+ try {
369
+ await setUserSetting(SETTING_DATA_REPO_URL, url);
370
+ }
371
+ catch {
372
+ // Non-fatal
373
+ }
374
+ }
375
+ else {
376
+ console.log(c.warning('GitHub repo creation failed.\n'));
377
+ }
378
+ }
379
+ }
380
+ else {
381
+ const remoteUrl = await prompt('Git remote URL for cross-machine sync (or press Enter to skip)');
382
+ if (remoteUrl) {
383
+ try {
384
+ const { execSync } = await import('child_process');
385
+ execSync(`git remote add origin ${remoteUrl}`, { cwd: resolvedDataDir, stdio: 'pipe' });
386
+ execSync('git push -u origin main', { cwd: resolvedDataDir, stdio: 'pipe' });
387
+ console.log(c.success(`Remote added and pushed.\n`));
388
+ try {
389
+ await setUserSetting(SETTING_DATA_REPO_URL, remoteUrl);
390
+ }
391
+ catch {
392
+ // Non-fatal
393
+ }
394
+ }
395
+ catch {
396
+ console.log(c.warning('Could not push to remote. You can push manually later.\n'));
397
+ }
398
+ }
399
+ else {
400
+ console.log(c.dim('Skipped remote setup.\n'));
401
+ }
402
+ }
403
+ }
404
+ // ── Step 4: Seed Document ─────────────────────────────────────────
405
+ console.log(c.bold('Step 4: Welcome Document\n'));
406
+ try {
407
+ const { getAllSources } = await import('../../core/vector-store.js');
408
+ const dbPath = path.join(resolvedDataDir, 'lore.lance');
409
+ const existing = await getAllSources(dbPath, { limit: 1 });
410
+ if (existing.length > 0) {
411
+ console.log(c.success('You already have documents indexed — skipping welcome doc.\n'));
412
+ }
413
+ else {
414
+ const { handleIngest } = await import('../../mcp/handlers/ingest.js');
415
+ const { WELCOME_DOC_CONTENT } = await import('../../core/data-repo.js');
416
+ console.log(c.dim('Indexing a welcome document so you can try search right away...'));
417
+ await handleIngest(dbPath, resolvedDataDir, {
418
+ content: WELCOME_DOC_CONTENT,
419
+ title: 'Getting Started with Lore',
420
+ project: 'lore',
421
+ source_type: 'document',
422
+ tags: ['getting-started', 'guide'],
423
+ }, { autoPush: true, hookContext: { mode: 'cli' } });
424
+ console.log(c.success('Indexed! Try: lore search "getting started"\n'));
425
+ }
426
+ }
427
+ catch (err) {
428
+ console.log(c.warning(`Skipped — ${err instanceof Error ? err.message : 'could not index welcome document'}`));
429
+ console.log(c.dim('You can add documents later with lore sync\n'));
430
+ }
431
+ // ── Step 5: Background Daemon ──────────────────────────────────────
432
+ console.log(c.bold('Step 5: Background Daemon\n'));
433
+ const startDaemon = await prompt('Start background sync daemon? (y/n)', 'y');
434
+ if (startDaemon.toLowerCase() === 'y') {
435
+ try {
436
+ const { startDaemonProcess } = await import('./sync.js');
437
+ const result = await startDaemonProcess(resolvedDataDir);
438
+ if (!result) {
439
+ console.log(c.warning('Could not start daemon. You can start it later with: lore sync start\n'));
440
+ }
441
+ else if (result.alreadyRunning) {
442
+ console.log(c.success(`Daemon already running (PID: ${result.pid})\n`));
443
+ }
444
+ else {
445
+ console.log(c.success(`Daemon started (PID: ${result.pid}). Watches for new files and auto-indexes.\n`));
446
+ }
447
+ }
448
+ catch (err) {
449
+ console.log(c.warning(`Could not start daemon: ${err instanceof Error ? err.message : err}`));
450
+ console.log(c.dim('You can start it later with: lore sync start\n'));
451
+ }
452
+ }
453
+ else {
454
+ console.log(c.dim('You can start it later with: lore sync start\n'));
455
+ }
456
+ // ── Step 6: Agent Skills ──────────────────────────────────────────
457
+ console.log(c.bold('Step 6: Agent Skills\n'));
458
+ console.log(c.dim('Lore works best when your AI agents know how to use it.'));
459
+ console.log(c.dim('Install instruction files so agents automatically search and ingest into Lore.\n'));
460
+ try {
461
+ const { interactiveSkillInstall } = await import('./skills.js');
462
+ const installed = await interactiveSkillInstall();
463
+ if (installed.length > 0) {
464
+ console.log(c.success(`\nInstalled skills: ${installed.join(', ')}\n`));
465
+ }
466
+ else {
467
+ console.log(c.dim('\nSkipped. You can install later with: lore skills install <name>\n'));
468
+ }
469
+ }
470
+ catch (err) {
471
+ console.log(c.warning(`Could not install skills: ${err instanceof Error ? err.message : err}`));
472
+ console.log(c.dim('You can install later with: lore skills install <name>\n'));
473
+ }
474
+ // ── Done ───────────────────────────────────────────────────────────
475
+ console.log(c.title('Setup complete!\n'));
476
+ console.log('Try these commands:');
477
+ console.log(c.list('lore search "getting started" — search your knowledge base'));
478
+ console.log(c.list('lore browse — interactive terminal browser'));
479
+ console.log(c.list('lore sync add — add a sync source directory'));
480
+ console.log(c.list('lore sync — sync documents now'));
481
+ console.log(c.list('lore skills list — see available agent skills'));
482
+ console.log('');
483
+ });
484
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Daemon Command
3
+ *
4
+ * Run lore watch as a background daemon with logging and status tracking.
5
+ */
6
+ import type { Command } from 'commander';
7
+ declare const CONFIG_DIR: string;
8
+ declare const PID_FILE: string;
9
+ declare const STATUS_FILE: string;
10
+ declare const LOG_FILE: string;
11
+ export interface DaemonStatus {
12
+ pid: number;
13
+ started_at: string;
14
+ last_sync?: string;
15
+ last_sync_result?: {
16
+ files_scanned: number;
17
+ files_processed: number;
18
+ errors: number;
19
+ };
20
+ }
21
+ export declare function registerDaemonCommand(program: Command, defaultDataDir: string): void;
22
+ export { CONFIG_DIR, PID_FILE, STATUS_FILE, LOG_FILE };