memax-cli 0.1.0-alpha.5 → 0.1.0-alpha.7
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/commands/delete.d.ts +1 -1
- package/dist/commands/delete.d.ts.map +1 -1
- package/dist/commands/delete.js +32 -4
- package/dist/commands/delete.js.map +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +4 -2
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/push.d.ts +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +6 -2
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +234 -18
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +93 -0
- package/dist/commands/sync.js.map +1 -1
- package/dist/index.js +20 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/delete.ts +43 -5
- package/src/commands/login.ts +4 -2
- package/src/commands/push.ts +12 -2
- package/src/commands/setup.ts +307 -22
- package/src/commands/sync.ts +103 -0
- package/src/index.ts +22 -2
package/src/commands/setup.ts
CHANGED
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
import { join, dirname } from "node:path";
|
|
10
10
|
import { homedir, platform } from "node:os";
|
|
11
11
|
import { execSync } from "node:child_process";
|
|
12
|
+
import { apiPost } from "../lib/api.js";
|
|
13
|
+
import { loadConfig } from "../lib/config.js";
|
|
12
14
|
|
|
13
15
|
// --- Agent definitions ---
|
|
14
16
|
|
|
@@ -25,7 +27,7 @@ interface AgentDef {
|
|
|
25
27
|
|
|
26
28
|
function getAgents(): AgentDef[] {
|
|
27
29
|
const home = homedir();
|
|
28
|
-
const
|
|
30
|
+
const cwd = process.cwd();
|
|
29
31
|
|
|
30
32
|
return [
|
|
31
33
|
{
|
|
@@ -97,6 +99,26 @@ function getAgents(): AgentDef[] {
|
|
|
97
99
|
hasHooks: false,
|
|
98
100
|
detect: () => existsSync(join(home, ".codex")) || commandExists("codex"),
|
|
99
101
|
},
|
|
102
|
+
{
|
|
103
|
+
name: "OpenClaw",
|
|
104
|
+
id: "openclaw",
|
|
105
|
+
configPath: join(home, ".openclaw", "openclaw.json"),
|
|
106
|
+
format: "json-mcpServers",
|
|
107
|
+
mcpKey: "mcp.servers",
|
|
108
|
+
hasHooks: false,
|
|
109
|
+
detect: () =>
|
|
110
|
+
existsSync(join(home, ".openclaw")) || commandExists("openclaw"),
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "OpenCode",
|
|
114
|
+
id: "opencode",
|
|
115
|
+
configPath: join(cwd, ".opencode", "opencode.jsonc"),
|
|
116
|
+
format: "json-mcpServers",
|
|
117
|
+
mcpKey: "mcp",
|
|
118
|
+
hasHooks: false,
|
|
119
|
+
detect: () =>
|
|
120
|
+
existsSync(join(cwd, ".opencode")) || commandExists("opencode"),
|
|
121
|
+
},
|
|
100
122
|
];
|
|
101
123
|
}
|
|
102
124
|
|
|
@@ -106,6 +128,8 @@ interface SetupOptions {
|
|
|
106
128
|
mcp?: boolean;
|
|
107
129
|
hooks?: boolean;
|
|
108
130
|
all?: boolean;
|
|
131
|
+
local?: boolean;
|
|
132
|
+
print?: boolean;
|
|
109
133
|
only?: string;
|
|
110
134
|
skip?: string;
|
|
111
135
|
}
|
|
@@ -114,20 +138,46 @@ export async function setupCommand(options: SetupOptions): Promise<void> {
|
|
|
114
138
|
const enableMcp = options.all || options.mcp;
|
|
115
139
|
const enableHooks = options.all || options.hooks;
|
|
116
140
|
|
|
117
|
-
if (!enableMcp && !enableHooks) {
|
|
141
|
+
if (!enableMcp && !enableHooks && !options.print) {
|
|
118
142
|
printUsage();
|
|
119
143
|
return;
|
|
120
144
|
}
|
|
121
145
|
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
146
|
+
// --print: just output config JSON for manual copy/paste
|
|
147
|
+
if (options.print) {
|
|
148
|
+
await printMcpConfigs(options.local ?? false);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Remote mode (default): need API key for auth
|
|
153
|
+
const useRemote = !options.local;
|
|
154
|
+
let apiKey: string | undefined;
|
|
155
|
+
|
|
156
|
+
if (useRemote && enableMcp) {
|
|
157
|
+
apiKey = await ensureApiKey();
|
|
158
|
+
if (!apiKey) {
|
|
159
|
+
console.error(
|
|
160
|
+
chalk.red(
|
|
161
|
+
"\n Could not create API key. Log in first: memax login\n" +
|
|
162
|
+
" Or use --local for local MCP server.\n",
|
|
163
|
+
),
|
|
164
|
+
);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Local mode: need memax binary
|
|
170
|
+
let memaxBin: MemaxBin | null = null;
|
|
171
|
+
if (!useRemote || enableHooks) {
|
|
172
|
+
memaxBin = resolveMemaxBin();
|
|
173
|
+
if (!memaxBin && !useRemote) {
|
|
174
|
+
console.error(
|
|
175
|
+
chalk.red(
|
|
176
|
+
"\n Could not find memax binary.\n Install globally: npm install -g memax-cli@alpha\n",
|
|
177
|
+
),
|
|
178
|
+
);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
131
181
|
}
|
|
132
182
|
|
|
133
183
|
// Filter agents
|
|
@@ -158,6 +208,11 @@ export async function setupCommand(options: SetupOptions): Promise<void> {
|
|
|
158
208
|
}
|
|
159
209
|
|
|
160
210
|
console.log(chalk.bold("\n Memax Setup\n"));
|
|
211
|
+
if (useRemote) {
|
|
212
|
+
console.log(chalk.gray(" Mode: remote server (recommended)\n"));
|
|
213
|
+
} else {
|
|
214
|
+
console.log(chalk.gray(" Mode: local CLI\n"));
|
|
215
|
+
}
|
|
161
216
|
|
|
162
217
|
const results: { agent: string; changes: string[] }[] = [];
|
|
163
218
|
|
|
@@ -167,8 +222,12 @@ export async function setupCommand(options: SetupOptions): Promise<void> {
|
|
|
167
222
|
// MCP setup
|
|
168
223
|
if (enableMcp) {
|
|
169
224
|
try {
|
|
170
|
-
|
|
171
|
-
|
|
225
|
+
if (useRemote) {
|
|
226
|
+
setupMcpRemote(agent, apiKey!);
|
|
227
|
+
} else {
|
|
228
|
+
setupMcp(agent, memaxBin!);
|
|
229
|
+
}
|
|
230
|
+
changes.push(useRemote ? "MCP server (remote)" : "MCP server (local)");
|
|
172
231
|
} catch (err) {
|
|
173
232
|
console.log(
|
|
174
233
|
chalk.red(
|
|
@@ -178,8 +237,8 @@ export async function setupCommand(options: SetupOptions): Promise<void> {
|
|
|
178
237
|
}
|
|
179
238
|
}
|
|
180
239
|
|
|
181
|
-
// Hook setup (only for agents that support it)
|
|
182
|
-
if (enableHooks && agent.hasHooks) {
|
|
240
|
+
// Hook setup (only for agents that support it — needs local binary)
|
|
241
|
+
if (enableHooks && agent.hasHooks && memaxBin) {
|
|
183
242
|
try {
|
|
184
243
|
setupHooks(agent, memaxBin);
|
|
185
244
|
changes.push("Context injection hook");
|
|
@@ -300,7 +359,182 @@ export async function teardownCommand(options: {
|
|
|
300
359
|
);
|
|
301
360
|
}
|
|
302
361
|
|
|
303
|
-
// --- MCP setup
|
|
362
|
+
// --- Remote MCP setup ---
|
|
363
|
+
|
|
364
|
+
async function ensureApiKey(): Promise<string | undefined> {
|
|
365
|
+
try {
|
|
366
|
+
const result = await apiPost<{ key: string }>("/v1/auth/api-keys", {
|
|
367
|
+
name: "mcp-setup",
|
|
368
|
+
expires_in_days: 0, // no expiry
|
|
369
|
+
});
|
|
370
|
+
return result.key;
|
|
371
|
+
} catch {
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function getApiUrl(): string {
|
|
377
|
+
return loadConfig().api_url;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function setupMcpRemote(agent: AgentDef, apiKey: string): void {
|
|
381
|
+
const mcpUrl = `${getApiUrl()}/mcp`;
|
|
382
|
+
|
|
383
|
+
// Claude Code uses its own CLI
|
|
384
|
+
if (agent.id === "claude-code") {
|
|
385
|
+
if (!commandExists("claude")) {
|
|
386
|
+
throw new Error("claude CLI not found in PATH");
|
|
387
|
+
}
|
|
388
|
+
try {
|
|
389
|
+
execSync("claude mcp remove memax", { stdio: "pipe" });
|
|
390
|
+
} catch {
|
|
391
|
+
// Not installed yet
|
|
392
|
+
}
|
|
393
|
+
// Claude Code HTTP transport
|
|
394
|
+
execSync(
|
|
395
|
+
`claude mcp add memax --transport http ${mcpUrl} --header "Authorization: Bearer ${apiKey}"`,
|
|
396
|
+
{ stdio: "pipe" },
|
|
397
|
+
);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Codex TOML
|
|
402
|
+
if (agent.format === "toml") {
|
|
403
|
+
mkdirSync(dirname(agent.configPath), { recursive: true });
|
|
404
|
+
let content = "";
|
|
405
|
+
if (existsSync(agent.configPath)) {
|
|
406
|
+
content = readFileSync(agent.configPath, "utf-8");
|
|
407
|
+
}
|
|
408
|
+
content = content.replace(/\[mcp_servers\.memax\][\s\S]*?(?=\n\[|$)/, "");
|
|
409
|
+
content = content.trim();
|
|
410
|
+
if (content) content += "\n\n";
|
|
411
|
+
content += `[mcp_servers.memax]\ntype = "url"\nurl = "${mcpUrl}"\n\n[mcp_servers.memax.headers]\nAuthorization = "Bearer ${apiKey}"\n`;
|
|
412
|
+
writeFileSync(agent.configPath, content);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// JSON-based agents
|
|
417
|
+
mkdirSync(dirname(agent.configPath), { recursive: true });
|
|
418
|
+
let config: Record<string, unknown> = {};
|
|
419
|
+
if (existsSync(agent.configPath)) {
|
|
420
|
+
try {
|
|
421
|
+
config = JSON.parse(readFileSync(agent.configPath, "utf-8"));
|
|
422
|
+
} catch {
|
|
423
|
+
// Start fresh
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const servers = (getNestedKey(config, agent.mcpKey) ?? {}) as Record<
|
|
428
|
+
string,
|
|
429
|
+
unknown
|
|
430
|
+
>;
|
|
431
|
+
servers.memax = {
|
|
432
|
+
type: "url",
|
|
433
|
+
url: mcpUrl,
|
|
434
|
+
headers: {
|
|
435
|
+
Authorization: `Bearer ${apiKey}`,
|
|
436
|
+
},
|
|
437
|
+
};
|
|
438
|
+
setNestedKey(config, agent.mcpKey, servers);
|
|
439
|
+
writeFileSync(agent.configPath, JSON.stringify(config, null, 2) + "\n");
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async function printMcpConfigs(local: boolean): Promise<void> {
|
|
443
|
+
const apiUrl = getApiUrl();
|
|
444
|
+
|
|
445
|
+
console.log(chalk.bold("\n Memax MCP Configuration\n"));
|
|
446
|
+
|
|
447
|
+
if (local) {
|
|
448
|
+
const bin = resolveMemaxBin();
|
|
449
|
+
const cmd = bin ? bin.command : "memax";
|
|
450
|
+
const args = bin ? [...bin.args, "mcp", "serve"] : ["mcp", "serve"];
|
|
451
|
+
|
|
452
|
+
console.log(chalk.gray(" Mode: local (stdio)\n"));
|
|
453
|
+
console.log(
|
|
454
|
+
chalk.white(" For most agents (Claude Code, Cursor, Gemini, etc.):\n"),
|
|
455
|
+
);
|
|
456
|
+
console.log(
|
|
457
|
+
JSON.stringify(
|
|
458
|
+
{
|
|
459
|
+
mcpServers: {
|
|
460
|
+
memax: { command: cmd, args },
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
null,
|
|
464
|
+
2,
|
|
465
|
+
)
|
|
466
|
+
.split("\n")
|
|
467
|
+
.map((l) => " " + l)
|
|
468
|
+
.join("\n"),
|
|
469
|
+
);
|
|
470
|
+
} else {
|
|
471
|
+
let apiKey: string | undefined;
|
|
472
|
+
try {
|
|
473
|
+
apiKey = await ensureApiKey();
|
|
474
|
+
} catch {
|
|
475
|
+
// Not logged in
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const keyDisplay = apiKey ?? "mxk_your_api_key_here";
|
|
479
|
+
const mcpUrl = `${apiUrl}/mcp`;
|
|
480
|
+
|
|
481
|
+
console.log(chalk.gray(" Mode: remote server (recommended)\n"));
|
|
482
|
+
|
|
483
|
+
console.log(chalk.white(" For Claude Code:\n"));
|
|
484
|
+
console.log(
|
|
485
|
+
chalk.gray(
|
|
486
|
+
` claude mcp add memax --transport http ${mcpUrl} --header "Authorization: Bearer ${keyDisplay}"`,
|
|
487
|
+
),
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
console.log(chalk.white("\n For Cursor, Copilot, Gemini, Windsurf:\n"));
|
|
491
|
+
console.log(
|
|
492
|
+
JSON.stringify(
|
|
493
|
+
{
|
|
494
|
+
mcpServers: {
|
|
495
|
+
memax: {
|
|
496
|
+
type: "url",
|
|
497
|
+
url: mcpUrl,
|
|
498
|
+
headers: {
|
|
499
|
+
Authorization: `Bearer ${keyDisplay}`,
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
null,
|
|
505
|
+
2,
|
|
506
|
+
)
|
|
507
|
+
.split("\n")
|
|
508
|
+
.map((l) => " " + l)
|
|
509
|
+
.join("\n"),
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
console.log(chalk.white("\n For Codex CLI (~/.codex/config.toml):\n"));
|
|
513
|
+
console.log(chalk.gray(` [mcp_servers.memax]`));
|
|
514
|
+
console.log(chalk.gray(` type = "url"`));
|
|
515
|
+
console.log(chalk.gray(` url = "${mcpUrl}"`));
|
|
516
|
+
console.log(chalk.gray(`\n [mcp_servers.memax.headers]`));
|
|
517
|
+
console.log(chalk.gray(` Authorization = "Bearer ${keyDisplay}"`));
|
|
518
|
+
|
|
519
|
+
if (apiKey) {
|
|
520
|
+
console.log(chalk.yellow("\n API key created: mcp-setup"));
|
|
521
|
+
console.log(chalk.gray(" Manage keys: memax auth list-keys"));
|
|
522
|
+
} else {
|
|
523
|
+
console.log(
|
|
524
|
+
chalk.yellow(
|
|
525
|
+
"\n Not logged in — replace mxk_your_api_key_here with a real key.",
|
|
526
|
+
),
|
|
527
|
+
);
|
|
528
|
+
console.log(
|
|
529
|
+
chalk.gray(" Run: memax login && memax auth create-key --name mcp"),
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
console.log();
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// --- Local MCP setup per agent ---
|
|
304
538
|
|
|
305
539
|
function setupMcp(agent: AgentDef, bin: MemaxBin): void {
|
|
306
540
|
// Claude Code has its own CLI for MCP management
|
|
@@ -326,12 +560,15 @@ function setupMcp(agent: AgentDef, bin: MemaxBin): void {
|
|
|
326
560
|
}
|
|
327
561
|
}
|
|
328
562
|
|
|
329
|
-
const servers = (config
|
|
563
|
+
const servers = (getNestedKey(config, agent.mcpKey) ?? {}) as Record<
|
|
564
|
+
string,
|
|
565
|
+
unknown
|
|
566
|
+
>;
|
|
330
567
|
servers.memax = {
|
|
331
568
|
command: bin.command,
|
|
332
569
|
args: [...bin.args, "mcp", "serve"],
|
|
333
570
|
};
|
|
334
|
-
config
|
|
571
|
+
setNestedKey(config, agent.mcpKey, servers);
|
|
335
572
|
|
|
336
573
|
writeFileSync(agent.configPath, JSON.stringify(config, null, 2) + "\n");
|
|
337
574
|
}
|
|
@@ -468,11 +705,12 @@ function removeMcpJson(agent: AgentDef): boolean {
|
|
|
468
705
|
|
|
469
706
|
try {
|
|
470
707
|
const config = JSON.parse(readFileSync(agent.configPath, "utf-8"));
|
|
471
|
-
const servers = config
|
|
708
|
+
const servers = getNestedKey(config, agent.mcpKey);
|
|
472
709
|
if (!servers?.memax) return false;
|
|
473
710
|
|
|
474
711
|
delete servers.memax;
|
|
475
|
-
if (Object.keys(servers).length === 0)
|
|
712
|
+
if (Object.keys(servers).length === 0)
|
|
713
|
+
deleteNestedKey(config, agent.mcpKey);
|
|
476
714
|
|
|
477
715
|
writeFileSync(agent.configPath, JSON.stringify(config, null, 2) + "\n");
|
|
478
716
|
console.log(chalk.gray(` Removed MCP from ${agent.name}`));
|
|
@@ -596,6 +834,46 @@ function commandExists(cmd: string): boolean {
|
|
|
596
834
|
}
|
|
597
835
|
}
|
|
598
836
|
|
|
837
|
+
// Nested key helpers for configs like openclaw's "mcp.servers"
|
|
838
|
+
function getNestedKey(
|
|
839
|
+
obj: Record<string, unknown>,
|
|
840
|
+
key: string,
|
|
841
|
+
): Record<string, unknown> | undefined {
|
|
842
|
+
const parts = key.split(".");
|
|
843
|
+
let current: unknown = obj;
|
|
844
|
+
for (const part of parts) {
|
|
845
|
+
if (current == null || typeof current !== "object") return undefined;
|
|
846
|
+
current = (current as Record<string, unknown>)[part];
|
|
847
|
+
}
|
|
848
|
+
return current as Record<string, unknown> | undefined;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function setNestedKey(
|
|
852
|
+
obj: Record<string, unknown>,
|
|
853
|
+
key: string,
|
|
854
|
+
value: unknown,
|
|
855
|
+
): void {
|
|
856
|
+
const parts = key.split(".");
|
|
857
|
+
let current: Record<string, unknown> = obj;
|
|
858
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
859
|
+
if (!(parts[i] in current) || typeof current[parts[i]] !== "object") {
|
|
860
|
+
current[parts[i]] = {};
|
|
861
|
+
}
|
|
862
|
+
current = current[parts[i]] as Record<string, unknown>;
|
|
863
|
+
}
|
|
864
|
+
current[parts[parts.length - 1]] = value;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function deleteNestedKey(obj: Record<string, unknown>, key: string): void {
|
|
868
|
+
const parts = key.split(".");
|
|
869
|
+
let current: Record<string, unknown> = obj;
|
|
870
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
871
|
+
if (!(parts[i] in current) || typeof current[parts[i]] !== "object") return;
|
|
872
|
+
current = current[parts[i]] as Record<string, unknown>;
|
|
873
|
+
}
|
|
874
|
+
delete current[parts[parts.length - 1]];
|
|
875
|
+
}
|
|
876
|
+
|
|
599
877
|
function writeHookScript(bin: MemaxBin): string {
|
|
600
878
|
const hooksDir = join(homedir(), ".memax", "hooks");
|
|
601
879
|
mkdirSync(hooksDir, { recursive: true });
|
|
@@ -634,14 +912,21 @@ function printUsage(): void {
|
|
|
634
912
|
console.log(chalk.gray(" Usage:\n"));
|
|
635
913
|
console.log(
|
|
636
914
|
chalk.gray(
|
|
637
|
-
" memax setup --mcp MCP
|
|
915
|
+
" memax setup --mcp Remote MCP server for all detected agents (default)",
|
|
638
916
|
),
|
|
639
917
|
);
|
|
640
918
|
console.log(
|
|
641
919
|
chalk.gray(" memax setup --all MCP + hooks (where supported)"),
|
|
642
920
|
);
|
|
921
|
+
console.log(
|
|
922
|
+
chalk.gray(
|
|
923
|
+
" memax setup --mcp --local Use local CLI instead of remote server",
|
|
924
|
+
),
|
|
925
|
+
);
|
|
926
|
+
console.log(
|
|
927
|
+
chalk.gray(" memax setup --print Print MCP config to copy/paste"),
|
|
928
|
+
);
|
|
643
929
|
console.log(chalk.gray(" memax setup --mcp --only claude-code,cursor"));
|
|
644
|
-
console.log(chalk.gray(" memax setup --all --skip codex"));
|
|
645
930
|
console.log(
|
|
646
931
|
chalk.gray(" memax teardown Remove all integrations\n"),
|
|
647
932
|
);
|
package/src/commands/sync.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
watch,
|
|
7
7
|
existsSync,
|
|
8
8
|
} from "node:fs";
|
|
9
|
+
import { createInterface } from "node:readline";
|
|
9
10
|
import { join, relative, extname, resolve, basename } from "node:path";
|
|
10
11
|
import { homedir } from "node:os";
|
|
11
12
|
import { apiPost } from "../lib/api.js";
|
|
@@ -17,6 +18,7 @@ interface SyncOptions {
|
|
|
17
18
|
watch?: boolean;
|
|
18
19
|
ignore?: string;
|
|
19
20
|
agentMemory?: boolean;
|
|
21
|
+
yes?: boolean;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
const DEFAULT_IGNORE = new Set([
|
|
@@ -85,6 +87,21 @@ export async function syncCommand(
|
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
console.log(chalk.gray(`Found ${files.length} files to sync`));
|
|
90
|
+
|
|
91
|
+
// Confirm if many files (>10) unless -y is passed
|
|
92
|
+
if (files.length > 10 && !options.yes) {
|
|
93
|
+
console.log(
|
|
94
|
+
chalk.yellow(
|
|
95
|
+
`\n This will push ${files.length} files. Continue? (y/N) `,
|
|
96
|
+
),
|
|
97
|
+
);
|
|
98
|
+
const confirmed = await confirmSync();
|
|
99
|
+
if (!confirmed) {
|
|
100
|
+
console.log(chalk.gray(" Cancelled.\n"));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
88
105
|
console.log();
|
|
89
106
|
|
|
90
107
|
let pushed = 0;
|
|
@@ -296,6 +313,79 @@ function discoverAgentMemoryFiles(): AgentMemoryLocation[] {
|
|
|
296
313
|
label: "./.codex/instructions.md",
|
|
297
314
|
path: join(cwd, ".codex", "instructions.md"),
|
|
298
315
|
});
|
|
316
|
+
locations.push({
|
|
317
|
+
label: "~/.codex/AGENTS.md",
|
|
318
|
+
path: join(home, ".codex", "AGENTS.md"),
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Gemini CLI
|
|
322
|
+
locations.push({
|
|
323
|
+
label: "~/.gemini/GEMINI.md",
|
|
324
|
+
path: join(home, ".gemini", "GEMINI.md"),
|
|
325
|
+
});
|
|
326
|
+
locations.push({ label: "./GEMINI.md", path: join(cwd, "GEMINI.md") });
|
|
327
|
+
|
|
328
|
+
// GitHub Copilot
|
|
329
|
+
locations.push({
|
|
330
|
+
label: "./.github/copilot-instructions.md",
|
|
331
|
+
path: join(cwd, ".github", "copilot-instructions.md"),
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Windsurf
|
|
335
|
+
locations.push({
|
|
336
|
+
label: "./.windsurfrules",
|
|
337
|
+
path: join(cwd, ".windsurfrules"),
|
|
338
|
+
});
|
|
339
|
+
const windsurfRulesDir = join(cwd, ".windsurf", "rules");
|
|
340
|
+
if (existsSync(windsurfRulesDir)) {
|
|
341
|
+
try {
|
|
342
|
+
for (const file of readdirSync(windsurfRulesDir)) {
|
|
343
|
+
if (file.endsWith(".md")) {
|
|
344
|
+
locations.push({
|
|
345
|
+
label: `./.windsurf/rules/${file}`,
|
|
346
|
+
path: join(windsurfRulesDir, file),
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
} catch {
|
|
351
|
+
// Skip on error
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// OpenClaw memory
|
|
356
|
+
const openclawMemoryDir = join(home, ".openclaw", "memory");
|
|
357
|
+
if (existsSync(openclawMemoryDir)) {
|
|
358
|
+
try {
|
|
359
|
+
for (const file of readdirSync(openclawMemoryDir)) {
|
|
360
|
+
if (file.endsWith(".md") || file.endsWith(".json")) {
|
|
361
|
+
locations.push({
|
|
362
|
+
label: `~/.openclaw/memory/${file}`,
|
|
363
|
+
path: join(openclawMemoryDir, file),
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
} catch {
|
|
368
|
+
// Skip on error
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// OpenCode (anomalyco) context
|
|
373
|
+
const opencodePath = join(cwd, ".opencode");
|
|
374
|
+
if (existsSync(opencodePath)) {
|
|
375
|
+
// Check for any markdown context files in .opencode/
|
|
376
|
+
try {
|
|
377
|
+
for (const file of readdirSync(opencodePath)) {
|
|
378
|
+
if (file.endsWith(".md")) {
|
|
379
|
+
locations.push({
|
|
380
|
+
label: `./.opencode/${file}`,
|
|
381
|
+
path: join(opencodePath, file),
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
} catch {
|
|
386
|
+
// Skip on error
|
|
387
|
+
}
|
|
388
|
+
}
|
|
299
389
|
|
|
300
390
|
// Generic agent config files in current directory
|
|
301
391
|
locations.push({ label: "./AGENTS.md", path: join(cwd, "AGENTS.md") });
|
|
@@ -401,3 +491,16 @@ async function syncAgentMemory(): Promise<void> {
|
|
|
401
491
|
),
|
|
402
492
|
);
|
|
403
493
|
}
|
|
494
|
+
|
|
495
|
+
function confirmSync(): Promise<boolean> {
|
|
496
|
+
return new Promise((resolve) => {
|
|
497
|
+
const rl = createInterface({
|
|
498
|
+
input: process.stdin,
|
|
499
|
+
output: process.stdout,
|
|
500
|
+
});
|
|
501
|
+
rl.question(" ", (answer) => {
|
|
502
|
+
rl.close();
|
|
503
|
+
resolve(answer.trim().toLowerCase() === "y");
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -39,7 +39,7 @@ program
|
|
|
39
39
|
// --- Core commands ---
|
|
40
40
|
|
|
41
41
|
program
|
|
42
|
-
.command("push")
|
|
42
|
+
.command("push [content]")
|
|
43
43
|
.description("Save knowledge to your Memax workspace")
|
|
44
44
|
.option("-f, --file <path>", "File to push")
|
|
45
45
|
.option("-c, --category <category>", "Category (auto-detected if omitted)")
|
|
@@ -77,7 +77,24 @@ program
|
|
|
77
77
|
program
|
|
78
78
|
.command("delete <id>")
|
|
79
79
|
.description("Delete a note")
|
|
80
|
-
.option("--
|
|
80
|
+
.option("-y, --yes", "Skip confirmation")
|
|
81
|
+
.action(deleteCommand);
|
|
82
|
+
|
|
83
|
+
// Aliases
|
|
84
|
+
program
|
|
85
|
+
.command("remember [content]")
|
|
86
|
+
.description("Alias for push — save knowledge to your Memax workspace")
|
|
87
|
+
.option("-f, --file <path>", "File to push")
|
|
88
|
+
.option("-c, --category <category>", "Category (auto-detected if omitted)")
|
|
89
|
+
.option("-t, --tags <tags>", "Comma-separated tags")
|
|
90
|
+
.option("--title <title>", "Note title")
|
|
91
|
+
.option("--stdin", "Read content from stdin")
|
|
92
|
+
.action(pushCommand);
|
|
93
|
+
|
|
94
|
+
program
|
|
95
|
+
.command("forget <id>")
|
|
96
|
+
.description("Alias for delete — remove a note from your workspace")
|
|
97
|
+
.option("-y, --yes", "Skip confirmation")
|
|
81
98
|
.action(deleteCommand);
|
|
82
99
|
|
|
83
100
|
// --- Sync ---
|
|
@@ -100,6 +117,7 @@ const syncCmd = program
|
|
|
100
117
|
"--agent-memory",
|
|
101
118
|
"Sync native AI agent memory files (Claude Code, Cursor, Codex)",
|
|
102
119
|
)
|
|
120
|
+
.option("-y, --yes", "Skip confirmation for large syncs")
|
|
103
121
|
.action(syncCommand);
|
|
104
122
|
|
|
105
123
|
syncCmd
|
|
@@ -115,6 +133,8 @@ program
|
|
|
115
133
|
.option("--mcp", "Enable MCP server (agent tools)")
|
|
116
134
|
.option("--hooks", "Enable context injection hooks")
|
|
117
135
|
.option("--all", "Enable both MCP and hooks")
|
|
136
|
+
.option("--local", "Use local stdio MCP instead of remote server")
|
|
137
|
+
.option("--print", "Print MCP config JSON to copy/paste (no changes made)")
|
|
118
138
|
.option("--only <agents>", "Only configure these agents (comma-separated)")
|
|
119
139
|
.option("--skip <agents>", "Skip these agents (comma-separated)")
|
|
120
140
|
.action(setupCommand);
|