memoire-ai 0.1.9 → 0.2.1

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/dist/cli.js CHANGED
@@ -26,12 +26,24 @@ const { values, positionals } = parseArgs({
26
26
  'user-email': { type: 'string' },
27
27
  'setup-token': { type: 'string' },
28
28
  'invite-token': { type: 'string' },
29
+ 'invite-code': { type: 'string' },
29
30
  'config-path': { type: 'string' },
30
31
  'client-id': { type: 'string' },
31
32
  port: { type: 'string' },
32
33
  'allow-origin': { type: 'string' },
33
34
  json: { type: 'boolean' },
34
35
  help: { type: 'boolean', short: 'h' },
36
+ // Wizard-specific flags
37
+ 'non-interactive': { type: 'boolean' },
38
+ 'skip-start': { type: 'boolean' },
39
+ cloud: { type: 'boolean' },
40
+ 'db-url': { type: 'string' },
41
+ 'db-url-direct': { type: 'string' },
42
+ 'openai-key': { type: 'string' },
43
+ 'anthropic-key': { type: 'string' },
44
+ agents: { type: 'string' },
45
+ 'api-port': { type: 'string' },
46
+ 'dashboard-port': { type: 'string' },
35
47
  },
36
48
  });
37
49
  const command = positionals[0];
@@ -40,342 +52,303 @@ if (values.help || !command) {
40
52
  memoire - Shared context layer for AI coding agents
41
53
 
42
54
  Usage:
43
- memoire setup Zero-config: auto-detect everything, bootstrap + install
44
- memoire login [options]
45
- memoire install [options]
46
- memoire uninstall Remove Memoire from this project
47
- memoire init [options]
48
- memoire search <query>
49
- memoire usage
50
- memoire doctor
51
- memoire profile
52
- memoire facts
53
- memoire resume [query]
54
- memoire handoff [query]
55
- memoire contexts [list]
56
- memoire contexts save [query]
57
- memoire contexts get <id>
58
- memoire manual [options]
59
- memoire hook <name>
60
- memoire status
61
- memoire dashboard [--port 4310]
62
- memoire local-tools [--port 4313]
63
- memoire mcp
55
+ memoire setup Interactive wizard zero to running in one command
56
+ memoire setup --cloud Use Memoire Cloud (no database needed)
57
+ memoire setup --db-url <url> Self-host with your own Postgres
58
+ memoire setup --agents cursor,codex Configure specific agents
59
+ memoire setup --non-interactive Skip prompts (requires all flags)
60
+ memoire login [options] Browser/device auth + install
61
+ memoire install [options] Manual install for a single agent
62
+ memoire uninstall Remove Memoire from this project
63
+ memoire search <query> Search shared memory
64
+ memoire usage Show usage stats
65
+ memoire doctor Diagnose issues
66
+ memoire profile Show project profile
67
+ memoire facts Show extracted facts
68
+ memoire resume [query] Resume previous work
69
+ memoire handoff [query] Create a handoff
70
+ memoire contexts [list|save|get] Manage saved contexts
71
+ memoire manual [options] Show manual setup instructions
72
+ memoire hook <name> Execute IDE hook
73
+ memoire status Check connection
74
+ memoire dashboard [--port 4310] Run local dashboard
75
+ memoire local-tools [--port 4313] Run local tools server
76
+ memoire mcp Start MCP server (stdio)
64
77
 
65
- Options:
78
+ Setup wizard flags:
79
+ --cloud Use Memoire Cloud instead of self-hosting
80
+ --db-url <url> Database URL (Postgres/Supabase transaction pooler)
81
+ --db-url-direct <url> Direct URL for migrations (session pooler, port 5432)
82
+ --openai-key <key> OpenAI API key (optional, for semantic embeddings)
83
+ --anthropic-key <key> Anthropic API key (optional, for AI summarization)
84
+ --agents <list> Comma-separated: cursor,claude,codex,amp
85
+ --api-port <port> API port (default: 3100)
86
+ --dashboard-port <port> Dashboard port (default: 3200)
87
+ --non-interactive Skip all prompts (for CI/scripting)
88
+ --skip-start Configure only, don't start API/dashboard
89
+
90
+ General options:
66
91
  --client, -c Client type: cursor | claude | codex | amp
67
92
  --api-key Memoire API key (sk_mem_...)
68
93
  --api-url API URL (default: production)
69
- --org-id Organization ID
70
- --project-id Project ID
94
+ --org-name Organization name
95
+ --project-name Project name
71
96
  --user-name Override detected user name
72
97
  --user-email Override detected user email
73
98
  --setup-token Setup token for bootstrapping additional orgs
74
- --invite-token Join an existing workspace invitation
75
- --org-name Organization name for init
76
- --org-slug Organization slug for init
77
- --project-name Project name for init
78
- --project-slug Project slug for init
79
- --config-path Override ~/.memoire/config.json for dashboard or MCP commands
99
+ --invite-token Join an existing workspace invitation (long hex token)
100
+ --invite-code Join using a short invite code (e.g. ABCD-1234)
101
+ --config-path Override config path for dashboard/MCP
80
102
  --client-id Override the registered client_id for hook or MCP commands
81
103
  --port Dashboard port (default: 4310)
82
104
  --allow-origin Extra dashboard origin allowed to call local tools helper
83
- --json Output as JSON (for piping and automation)
105
+ --json Output as JSON
84
106
  --help, -h Show this help
85
107
 
86
108
  Examples:
87
- npx memoire setup # Zero-config one-liner
88
- npx memoire setup --api-url http://localhost:3100 # Self-hosted
89
- npx memoire login # Browser/device auth + install
90
- npx memoire init --client cursor --api-url http://localhost:3100 --org-name Acme
91
- npx memoire init --client cursor --api-url http://localhost:3100 --invite-token <token>
92
- npx memoire install --client cursor --api-key sk_mem_xxx
93
- npx memoire install --client claude --api-key sk_mem_xxx --api-url http://localhost:3100
109
+ npx memoire setup # Interactive wizard
110
+ npx memoire setup --cloud # Cloud hosted
111
+ npx memoire setup --db-url "postgresql://..." --agents cursor,codex # Self-hosted
112
+ npx memoire login # Browser auth
94
113
  npx memoire search "auth decisions"
95
- npx memoire usage
96
114
  npx memoire doctor
97
- npx memoire profile
98
- npx memoire facts
99
- npx memoire resume "drizzle migrations"
100
- npx memoire handoff "oauth callbacks"
101
- npx memoire contexts
102
- npx memoire contexts save "oauth callbacks"
103
- npx memoire contexts get handoff-123
104
- npx memoire manual --client codex
105
- npx memoire hook cursor-before-submit --client cursor
106
- npx memoire status
107
- npx memoire dashboard --port 4310
108
- npx memoire local-tools --port 4313 --allow-origin https://dashboard.example.com
109
- npx memoire mcp
110
115
  `);
111
116
  process.exit(0);
112
117
  }
113
- if (command === 'setup') {
114
- // Zero-config setup: auto-detect client, health-check API, bootstrap + install
115
- const apiUrl = values['api-url'];
116
- console.log('\n Memoire Zero-Config Setup\n');
117
- // 1. Auto-detect client
118
- const client = values.client ?? (await detectClient());
119
- if (!client) {
120
- console.error(' Could not detect AI client (Cursor, Claude Code, Codex).');
121
- console.error(' Use --client to specify one: memoire setup --client cursor');
122
- process.exit(1);
123
- }
124
- console.log(` Detected client: ${client}`);
125
- // 2. Health check API
126
- console.log(` Checking API at ${apiUrl}...`);
127
- try {
128
- const healthRes = await fetch(`${apiUrl.replace(/\/$/, '')}/health`, {
129
- signal: AbortSignal.timeout(5000),
118
+ try {
119
+ if (command === 'setup') {
120
+ const { runSetupWizard } = await import('./setup-wizard.js');
121
+ const defaultApiUrl = 'http://esprit-prod-alb-1025392673.us-east-1.elb.amazonaws.com';
122
+ await runSetupWizard({
123
+ apiUrl: values['api-url'] !== defaultApiUrl ? values['api-url'] : undefined,
124
+ cloud: values.cloud,
125
+ dbUrl: values['db-url'],
126
+ dbUrlDirect: values['db-url-direct'],
127
+ openaiKey: values['openai-key'],
128
+ anthropicKey: values['anthropic-key'],
129
+ agents: values.agents,
130
+ inviteCode: values['invite-code'],
131
+ orgName: values['org-name'],
132
+ orgSlug: values['org-slug'],
133
+ projectName: values['project-name'],
134
+ projectSlug: values['project-slug'],
135
+ userName: values['user-name'],
136
+ userEmail: values['user-email'],
137
+ setupToken: values['setup-token'],
138
+ inviteToken: values['invite-token'],
139
+ apiPort: values['api-port'] ? parseInt(values['api-port'], 10) : undefined,
140
+ dashboardPort: values['dashboard-port'] ? parseInt(values['dashboard-port'], 10) : undefined,
141
+ nonInteractive: values['non-interactive'],
142
+ skipStart: values['skip-start'],
143
+ json: values.json,
130
144
  });
131
- if (!healthRes.ok) {
132
- console.error(` API returned ${healthRes.status}. Is the Memoire API running?`);
133
- process.exit(1);
134
- }
135
- console.log(' API is healthy');
136
- }
137
- catch {
138
- console.error(` Could not reach API at ${apiUrl}`);
139
- console.error(' Start the API first or use --api-url to specify the correct URL');
140
- process.exit(1);
141
145
  }
142
- // 3. Check for existing config
143
- const { existsSync } = await import('node:fs');
144
- const { join } = await import('node:path');
145
- const { homedir } = await import('node:os');
146
- const existingConfig = join(homedir(), '.memoire', 'config.json');
147
- if (existsSync(existingConfig) && !values['api-key']) {
148
- console.log(' Found existing config at ~/.memoire/config.json');
149
- try {
150
- const { readFileSync } = await import('node:fs');
151
- const config = JSON.parse(readFileSync(existingConfig, 'utf-8'));
152
- if (config.api_key && config.org_id) {
153
- console.log(' Re-installing with existing credentials...');
154
- await install({
155
- client: client,
156
- apiKey: config.api_key,
157
- apiUrl: config.api_url ?? apiUrl,
158
- orgId: config.org_id,
159
- projectId: config.project_id,
160
- userName: values['user-name'],
161
- userEmail: values['user-email'],
162
- });
163
- process.exit(0);
164
- }
146
+ else if (command === 'install') {
147
+ const client = values.client ?? (await detectClient());
148
+ if (!client) {
149
+ console.error('Could not detect client. Use --client to specify one.');
150
+ process.exit(1);
165
151
  }
166
- catch {
167
- // Config unreadable, proceed with fresh init
152
+ const apiKey = values['api-key'];
153
+ if (!apiKey) {
154
+ console.error('API key required. Use --api-key to provide one.');
155
+ process.exit(1);
168
156
  }
157
+ await install({
158
+ client: client,
159
+ apiKey,
160
+ apiUrl: values['api-url'],
161
+ orgId: values['org-id'],
162
+ projectId: values['project-id'],
163
+ userName: values['user-name'],
164
+ userEmail: values['user-email'],
165
+ });
169
166
  }
170
- // 4. Bootstrap fresh workspace
171
- console.log(' Bootstrapping new workspace...');
172
- await init({
173
- client: client,
174
- apiUrl,
175
- setupToken: values['setup-token'],
176
- inviteToken: values['invite-token'],
177
- orgName: values['org-name'],
178
- orgSlug: values['org-slug'],
179
- projectName: values['project-name'],
180
- projectSlug: values['project-slug'],
181
- userName: values['user-name'],
182
- userEmail: values['user-email'],
183
- });
184
- console.log('\n Setup complete! Memoire is ready.\n');
185
- }
186
- else if (command === 'install') {
187
- const client = values.client ?? (await detectClient());
188
- if (!client) {
189
- console.error('Could not detect client. Use --client to specify one.');
190
- process.exit(1);
191
- }
192
- const apiKey = values['api-key'];
193
- if (!apiKey) {
194
- console.error('API key required. Use --api-key to provide one.');
195
- process.exit(1);
196
- }
197
- await install({
198
- client: client,
199
- apiKey,
200
- apiUrl: values['api-url'],
201
- orgId: values['org-id'],
202
- projectId: values['project-id'],
203
- userName: values['user-name'],
204
- userEmail: values['user-email'],
205
- });
206
- }
207
- else if (command === 'uninstall') {
208
- await uninstall();
209
- }
210
- else if (command === 'login') {
211
- await runLoginCommand({
212
- client: values.client,
213
- apiUrl: values['api-url'],
214
- userName: values['user-name'],
215
- userEmail: values['user-email'],
216
- });
217
- }
218
- else if (command === 'init') {
219
- const client = values.client ?? (await detectClient());
220
- if (!client) {
221
- console.error('Could not detect client. Use --client to specify one.');
222
- process.exit(1);
223
- }
224
- await init({
225
- client: client,
226
- apiUrl: values['api-url'],
227
- setupToken: values['setup-token'],
228
- inviteToken: values['invite-token'],
229
- orgName: values['org-name'],
230
- orgSlug: values['org-slug'],
231
- projectName: values['project-name'],
232
- projectSlug: values['project-slug'],
233
- userName: values['user-name'],
234
- userEmail: values['user-email'],
235
- });
236
- }
237
- else if (command === 'search') {
238
- const query = positionals.slice(1).join(' ').trim();
239
- if (!query) {
240
- console.error('Search query required. Example: memoire search "auth decisions"');
241
- process.exit(1);
242
- }
243
- if (values.json) {
244
- console.log(JSON.stringify(await runSearchCommandJson(query), null, 2));
245
- }
246
- else {
247
- console.log(await runSearchCommand(query));
167
+ else if (command === 'uninstall') {
168
+ await uninstall();
248
169
  }
249
- }
250
- else if (command === 'status') {
251
- if (values.json) {
252
- console.log(JSON.stringify(await runStatusCommandJson(), null, 2));
170
+ else if (command === 'login') {
171
+ await runLoginCommand({
172
+ client: values.client,
173
+ apiUrl: values['api-url'],
174
+ userName: values['user-name'],
175
+ userEmail: values['user-email'],
176
+ });
253
177
  }
254
- else {
255
- console.log(await runStatusCommand());
178
+ else if (command === 'init') {
179
+ const client = values.client ?? (await detectClient());
180
+ if (!client) {
181
+ console.error('Could not detect client. Use --client to specify one.');
182
+ process.exit(1);
183
+ }
184
+ await init({
185
+ client: client,
186
+ apiUrl: values['api-url'],
187
+ setupToken: values['setup-token'],
188
+ inviteToken: values['invite-token'],
189
+ inviteCode: values['invite-code'],
190
+ orgName: values['org-name'],
191
+ orgSlug: values['org-slug'],
192
+ projectName: values['project-name'],
193
+ projectSlug: values['project-slug'],
194
+ userName: values['user-name'],
195
+ userEmail: values['user-email'],
196
+ });
256
197
  }
257
- }
258
- else if (command === 'usage') {
259
- if (values.json) {
260
- console.log(JSON.stringify(await runUsageCommandJson(), null, 2));
198
+ else if (command === 'search') {
199
+ const query = positionals.slice(1).join(' ').trim();
200
+ if (!query) {
201
+ console.error('Search query required. Example: memoire search "auth decisions"');
202
+ process.exit(1);
203
+ }
204
+ if (values.json) {
205
+ console.log(JSON.stringify(await runSearchCommandJson(query), null, 2));
206
+ }
207
+ else {
208
+ console.log(await runSearchCommand(query));
209
+ }
261
210
  }
262
- else {
263
- console.log(await runUsageCommand());
211
+ else if (command === 'status') {
212
+ if (values.json) {
213
+ console.log(JSON.stringify(await runStatusCommandJson(), null, 2));
214
+ }
215
+ else {
216
+ console.log(await runStatusCommand());
217
+ }
264
218
  }
265
- }
266
- else if (command === 'doctor') {
267
- if (values.json) {
268
- console.log(JSON.stringify(await runDoctorCommandJson(), null, 2));
219
+ else if (command === 'usage') {
220
+ if (values.json) {
221
+ console.log(JSON.stringify(await runUsageCommandJson(), null, 2));
222
+ }
223
+ else {
224
+ console.log(await runUsageCommand());
225
+ }
269
226
  }
270
- else {
271
- console.log(await runDoctorCommand());
227
+ else if (command === 'doctor') {
228
+ if (values.json) {
229
+ console.log(JSON.stringify(await runDoctorCommandJson(), null, 2));
230
+ }
231
+ else {
232
+ console.log(await runDoctorCommand());
233
+ }
272
234
  }
273
- }
274
- else if (command === 'profile') {
275
- if (values.json) {
276
- console.log(JSON.stringify(await runProfileCommandJson(), null, 2));
235
+ else if (command === 'profile') {
236
+ if (values.json) {
237
+ console.log(JSON.stringify(await runProfileCommandJson(), null, 2));
238
+ }
239
+ else {
240
+ console.log(await runProfileCommand());
241
+ }
277
242
  }
278
- else {
279
- console.log(await runProfileCommand());
243
+ else if (command === 'facts') {
244
+ if (values.json) {
245
+ console.log(JSON.stringify(await runFactsCommandJson(), null, 2));
246
+ }
247
+ else {
248
+ console.log(await runFactsCommand());
249
+ }
280
250
  }
281
- }
282
- else if (command === 'facts') {
283
- if (values.json) {
284
- console.log(JSON.stringify(await runFactsCommandJson(), null, 2));
251
+ else if (command === 'resume') {
252
+ console.log(await runResumeCommand(positionals.slice(1).join(' ')));
285
253
  }
286
- else {
287
- console.log(await runFactsCommand());
254
+ else if (command === 'handoff') {
255
+ console.log(await runHandoffCommand(positionals.slice(1).join(' ')));
288
256
  }
289
- }
290
- else if (command === 'resume') {
291
- console.log(await runResumeCommand(positionals.slice(1).join(' ')));
292
- }
293
- else if (command === 'handoff') {
294
- console.log(await runHandoffCommand(positionals.slice(1).join(' ')));
295
- }
296
- else if (command === 'contexts') {
297
- const subcommand = positionals[1] ?? 'list';
298
- if (subcommand === 'list') {
299
- if (values.json) {
300
- console.log(JSON.stringify(await runContextsListCommandJson(), null, 2));
257
+ else if (command === 'contexts') {
258
+ const subcommand = positionals[1] ?? 'list';
259
+ if (subcommand === 'list') {
260
+ if (values.json) {
261
+ console.log(JSON.stringify(await runContextsListCommandJson(), null, 2));
262
+ }
263
+ else {
264
+ console.log(await runContextsListCommand());
265
+ }
266
+ }
267
+ else if (subcommand === 'save') {
268
+ console.log(await runHandoffCommand(positionals.slice(2).join(' ')));
269
+ }
270
+ else if (subcommand === 'get') {
271
+ const handoffId = positionals[2]?.trim();
272
+ if (!handoffId) {
273
+ console.error('Context ID required. Example: memoire contexts get <id>');
274
+ process.exit(1);
275
+ }
276
+ if (values.json) {
277
+ console.log(JSON.stringify(await runContextsGetCommandJson(handoffId), null, 2));
278
+ }
279
+ else {
280
+ console.log(await runContextsGetCommand(handoffId));
281
+ }
301
282
  }
302
283
  else {
303
- console.log(await runContextsListCommand());
284
+ console.error(`Unknown contexts command: ${subcommand}`);
285
+ console.error('Use: memoire contexts [list|save|get]');
286
+ process.exit(1);
304
287
  }
305
288
  }
306
- else if (subcommand === 'save') {
307
- console.log(await runHandoffCommand(positionals.slice(2).join(' ')));
289
+ else if (command === 'manual') {
290
+ console.log(await runManualSetupCommand({
291
+ client: values.client,
292
+ configPath: values['config-path'],
293
+ }));
308
294
  }
309
- else if (subcommand === 'get') {
310
- const handoffId = positionals[2]?.trim();
311
- if (!handoffId) {
312
- console.error('Context ID required. Example: memoire contexts get <id>');
295
+ else if (command === 'dashboard') {
296
+ const parsedPort = values.port ? Number.parseInt(values.port, 10) : undefined;
297
+ if (values.port && !Number.isFinite(parsedPort)) {
298
+ console.error('Port must be a number.');
313
299
  process.exit(1);
314
300
  }
315
- if (values.json) {
316
- console.log(JSON.stringify(await runContextsGetCommandJson(handoffId), null, 2));
301
+ await runDashboardCommand({
302
+ port: parsedPort,
303
+ configPath: values['config-path'],
304
+ });
305
+ }
306
+ else if (command === 'local-tools') {
307
+ const parsedPort = values.port ? Number.parseInt(values.port, 10) : undefined;
308
+ if (values.port && !Number.isFinite(parsedPort)) {
309
+ console.error('Port must be a number.');
310
+ process.exit(1);
317
311
  }
318
- else {
319
- console.log(await runContextsGetCommand(handoffId));
312
+ await runLocalToolsCommand({
313
+ port: parsedPort,
314
+ configPath: values['config-path'],
315
+ allowOrigin: values['allow-origin'],
316
+ });
317
+ }
318
+ else if (command === 'mcp') {
319
+ await runMcpCommand({
320
+ ...process.env,
321
+ ...(values['config-path'] ? { MEMOIRE_CONFIG_PATH: values['config-path'] } : {}),
322
+ ...(values['client-id'] ? { MEMOIRE_CLIENT_ID: values['client-id'] } : {}),
323
+ });
324
+ }
325
+ else if (command === 'hook') {
326
+ const hookName = positionals[1];
327
+ if (!hookName) {
328
+ console.error('Hook name required. Example: memoire hook cursor-before-submit --client cursor');
329
+ process.exit(1);
320
330
  }
331
+ await runHookCommand({
332
+ hookName,
333
+ client: values.client,
334
+ clientId: values['client-id'],
335
+ configPath: values['config-path'],
336
+ });
321
337
  }
322
338
  else {
323
- console.error(`Unknown contexts command: ${subcommand}`);
324
- console.error('Use: memoire contexts [list|save|get]');
325
- process.exit(1);
326
- }
327
- }
328
- else if (command === 'manual') {
329
- console.log(await runManualSetupCommand({
330
- client: values.client,
331
- configPath: values['config-path'],
332
- }));
333
- }
334
- else if (command === 'dashboard') {
335
- const parsedPort = values.port ? Number.parseInt(values.port, 10) : undefined;
336
- if (values.port && !Number.isFinite(parsedPort)) {
337
- console.error('Port must be a number.');
339
+ console.error(`Unknown command: ${command}`);
338
340
  process.exit(1);
339
341
  }
340
- await runDashboardCommand({
341
- port: parsedPort,
342
- configPath: values['config-path'],
343
- });
344
342
  }
345
- else if (command === 'local-tools') {
346
- const parsedPort = values.port ? Number.parseInt(values.port, 10) : undefined;
347
- if (values.port && !Number.isFinite(parsedPort)) {
348
- console.error('Port must be a number.');
349
- process.exit(1);
343
+ catch (err) {
344
+ const msg = err instanceof Error ? err.message : String(err);
345
+ if (msg.includes('config not found') || msg.includes('Not authenticated')) {
346
+ console.error(`\n ${msg}\n`);
347
+ console.error(' Run: npx memoire-ai setup\n');
350
348
  }
351
- await runLocalToolsCommand({
352
- port: parsedPort,
353
- configPath: values['config-path'],
354
- allowOrigin: values['allow-origin'],
355
- });
356
- }
357
- else if (command === 'mcp') {
358
- await runMcpCommand({
359
- ...process.env,
360
- ...(values['config-path'] ? { MEMOIRE_CONFIG_PATH: values['config-path'] } : {}),
361
- ...(values['client-id'] ? { MEMOIRE_CLIENT_ID: values['client-id'] } : {}),
362
- });
363
- }
364
- else if (command === 'hook') {
365
- const hookName = positionals[1];
366
- if (!hookName) {
367
- console.error('Hook name required. Example: memoire hook cursor-before-submit --client cursor');
368
- process.exit(1);
349
+ else {
350
+ console.error(`\n Error: ${msg}\n`);
369
351
  }
370
- await runHookCommand({
371
- hookName,
372
- client: values.client,
373
- clientId: values['client-id'],
374
- configPath: values['config-path'],
375
- });
376
- }
377
- else {
378
- console.error(`Unknown command: ${command}`);
379
352
  process.exit(1);
380
353
  }
381
354
  //# sourceMappingURL=cli.js.map