@codedir/mimir-code 0.1.1 → 0.1.2
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.mjs +556 -16
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
1
3
|
// src/cli.ts
|
|
2
4
|
import { Command } from "commander";
|
|
3
5
|
|
|
@@ -5,28 +7,28 @@ import { Command } from "commander";
|
|
|
5
7
|
import fs from "fs/promises";
|
|
6
8
|
import fg from "fast-glob";
|
|
7
9
|
var FileSystemAdapter = class {
|
|
8
|
-
async readFile(
|
|
9
|
-
return fs.readFile(
|
|
10
|
+
async readFile(path9, encoding = "utf-8") {
|
|
11
|
+
return fs.readFile(path9, encoding);
|
|
10
12
|
}
|
|
11
|
-
async writeFile(
|
|
12
|
-
await fs.writeFile(
|
|
13
|
+
async writeFile(path9, content, encoding = "utf-8") {
|
|
14
|
+
await fs.writeFile(path9, content, encoding);
|
|
13
15
|
}
|
|
14
|
-
async exists(
|
|
16
|
+
async exists(path9) {
|
|
15
17
|
try {
|
|
16
|
-
await fs.access(
|
|
18
|
+
await fs.access(path9);
|
|
17
19
|
return true;
|
|
18
20
|
} catch {
|
|
19
21
|
return false;
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
|
-
async mkdir(
|
|
23
|
-
await fs.mkdir(
|
|
24
|
+
async mkdir(path9, options) {
|
|
25
|
+
await fs.mkdir(path9, options);
|
|
24
26
|
}
|
|
25
|
-
async readdir(
|
|
26
|
-
return fs.readdir(
|
|
27
|
+
async readdir(path9) {
|
|
28
|
+
return fs.readdir(path9);
|
|
27
29
|
}
|
|
28
|
-
async stat(
|
|
29
|
-
const stats = await fs.stat(
|
|
30
|
+
async stat(path9) {
|
|
31
|
+
const stats = await fs.stat(path9);
|
|
30
32
|
return {
|
|
31
33
|
isFile: () => stats.isFile(),
|
|
32
34
|
isDirectory: () => stats.isDirectory(),
|
|
@@ -34,11 +36,11 @@ var FileSystemAdapter = class {
|
|
|
34
36
|
mtime: stats.mtime
|
|
35
37
|
};
|
|
36
38
|
}
|
|
37
|
-
async unlink(
|
|
38
|
-
await fs.unlink(
|
|
39
|
+
async unlink(path9) {
|
|
40
|
+
await fs.unlink(path9);
|
|
39
41
|
}
|
|
40
|
-
async rmdir(
|
|
41
|
-
await fs.rmdir(
|
|
42
|
+
async rmdir(path9, options) {
|
|
43
|
+
await fs.rmdir(path9, options);
|
|
42
44
|
}
|
|
43
45
|
async copyFile(src, dest) {
|
|
44
46
|
await fs.copyFile(src, dest);
|
|
@@ -51,6 +53,94 @@ var FileSystemAdapter = class {
|
|
|
51
53
|
}
|
|
52
54
|
};
|
|
53
55
|
|
|
56
|
+
// src/platform/ProcessExecutorAdapter.ts
|
|
57
|
+
import { execa, execaCommand } from "execa";
|
|
58
|
+
var ProcessExecutorAdapter = class {
|
|
59
|
+
/**
|
|
60
|
+
* Execute command and wait for completion
|
|
61
|
+
*/
|
|
62
|
+
async execute(command, args = [], options = {}) {
|
|
63
|
+
try {
|
|
64
|
+
const result = await execa(command, args, {
|
|
65
|
+
cwd: options.cwd,
|
|
66
|
+
env: options.env,
|
|
67
|
+
timeout: options.timeout,
|
|
68
|
+
shell: options.shell,
|
|
69
|
+
input: options.input,
|
|
70
|
+
reject: false
|
|
71
|
+
// Don't throw on non-zero exit codes
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
stdout: result.stdout,
|
|
75
|
+
stderr: result.stderr,
|
|
76
|
+
exitCode: result.exitCode ?? (result.failed ? 1 : 0),
|
|
77
|
+
command: result.command,
|
|
78
|
+
timedOut: result.timedOut ?? false
|
|
79
|
+
};
|
|
80
|
+
} catch (error) {
|
|
81
|
+
const exitCode = error.code === "ENOENT" ? 127 : error.exitCode || 1;
|
|
82
|
+
return {
|
|
83
|
+
stdout: error.stdout || "",
|
|
84
|
+
stderr: error.stderr || error.message || String(error),
|
|
85
|
+
exitCode,
|
|
86
|
+
command: error.command || `${command} ${args.join(" ")}`,
|
|
87
|
+
timedOut: error.timedOut ?? false
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Spawn process (doesn't wait for completion)
|
|
93
|
+
*/
|
|
94
|
+
spawn(command, args = [], options = {}) {
|
|
95
|
+
const subprocess = execa(command, args, {
|
|
96
|
+
cwd: options.cwd,
|
|
97
|
+
env: options.env,
|
|
98
|
+
timeout: options.timeout,
|
|
99
|
+
shell: options.shell,
|
|
100
|
+
stdin: options.input ? "pipe" : "inherit",
|
|
101
|
+
stdout: "pipe",
|
|
102
|
+
stderr: "pipe"
|
|
103
|
+
});
|
|
104
|
+
if (options.input && subprocess.stdin) {
|
|
105
|
+
subprocess.stdin.write(options.input);
|
|
106
|
+
subprocess.stdin.end();
|
|
107
|
+
}
|
|
108
|
+
return subprocess;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Execute command in shell
|
|
112
|
+
*/
|
|
113
|
+
async executeShell(command, options = {}) {
|
|
114
|
+
try {
|
|
115
|
+
const result = await execaCommand(command, {
|
|
116
|
+
cwd: options.cwd,
|
|
117
|
+
env: options.env,
|
|
118
|
+
timeout: options.timeout,
|
|
119
|
+
shell: true,
|
|
120
|
+
input: options.input,
|
|
121
|
+
reject: false
|
|
122
|
+
// Don't throw on non-zero exit codes
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
stdout: result.stdout,
|
|
126
|
+
stderr: result.stderr,
|
|
127
|
+
exitCode: result.exitCode ?? (result.failed ? 1 : 0),
|
|
128
|
+
command: result.command,
|
|
129
|
+
timedOut: result.timedOut ?? false
|
|
130
|
+
};
|
|
131
|
+
} catch (error) {
|
|
132
|
+
const exitCode = error.code === "ENOENT" ? 127 : error.exitCode || 1;
|
|
133
|
+
return {
|
|
134
|
+
stdout: error.stdout || "",
|
|
135
|
+
stderr: error.stderr || error.message || String(error),
|
|
136
|
+
exitCode,
|
|
137
|
+
command: error.command || command,
|
|
138
|
+
timedOut: error.timedOut ?? false
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
54
144
|
// src/config/schemas.ts
|
|
55
145
|
import { z } from "zod";
|
|
56
146
|
var ThemeSchema = z.enum([
|
|
@@ -6058,13 +6148,457 @@ var InitCommand = class {
|
|
|
6058
6148
|
}
|
|
6059
6149
|
};
|
|
6060
6150
|
|
|
6151
|
+
// src/cli/commands/UninstallCommand.ts
|
|
6152
|
+
import path8 from "path";
|
|
6153
|
+
import os6 from "os";
|
|
6154
|
+
import React11 from "react";
|
|
6155
|
+
import { render as render3, Box as Box12, Text as Text11 } from "ink";
|
|
6156
|
+
import Spinner from "ink-spinner";
|
|
6157
|
+
import TextInput2 from "ink-text-input";
|
|
6158
|
+
var UninstallCommand = class {
|
|
6159
|
+
constructor(fs4, executor2) {
|
|
6160
|
+
this.fs = fs4;
|
|
6161
|
+
this.executor = executor2;
|
|
6162
|
+
}
|
|
6163
|
+
async execute(options = {}) {
|
|
6164
|
+
try {
|
|
6165
|
+
const autoConfirm = options.yes || options.quiet;
|
|
6166
|
+
let confirmed = autoConfirm;
|
|
6167
|
+
if (!autoConfirm) {
|
|
6168
|
+
confirmed = await this.promptConfirmation();
|
|
6169
|
+
}
|
|
6170
|
+
if (!confirmed) {
|
|
6171
|
+
if (!options.quiet) {
|
|
6172
|
+
logger.info("Uninstall cancelled.");
|
|
6173
|
+
}
|
|
6174
|
+
return;
|
|
6175
|
+
}
|
|
6176
|
+
let keepConfig;
|
|
6177
|
+
if (options.removeConfig !== void 0) {
|
|
6178
|
+
keepConfig = !options.removeConfig;
|
|
6179
|
+
} else if (options.keepConfig !== void 0) {
|
|
6180
|
+
keepConfig = options.keepConfig;
|
|
6181
|
+
} else if (autoConfirm) {
|
|
6182
|
+
keepConfig = true;
|
|
6183
|
+
} else {
|
|
6184
|
+
keepConfig = await this.promptKeepConfig();
|
|
6185
|
+
}
|
|
6186
|
+
const result = await this.uninstall(keepConfig, options.quiet);
|
|
6187
|
+
if (!options.quiet) {
|
|
6188
|
+
this.printSummary(result);
|
|
6189
|
+
}
|
|
6190
|
+
if (!result.success) {
|
|
6191
|
+
process.exit(1);
|
|
6192
|
+
}
|
|
6193
|
+
} catch (error) {
|
|
6194
|
+
if (!options.quiet) {
|
|
6195
|
+
logger.error("Uninstall failed", { error });
|
|
6196
|
+
}
|
|
6197
|
+
process.exit(1);
|
|
6198
|
+
}
|
|
6199
|
+
}
|
|
6200
|
+
async promptConfirmation() {
|
|
6201
|
+
return new Promise((resolve) => {
|
|
6202
|
+
const ConfirmPrompt = () => {
|
|
6203
|
+
const [input, setInput] = React11.useState("");
|
|
6204
|
+
const [submitted, setSubmitted] = React11.useState(false);
|
|
6205
|
+
React11.useEffect(() => {
|
|
6206
|
+
if (submitted) {
|
|
6207
|
+
const answer = input.toLowerCase();
|
|
6208
|
+
resolve(answer === "y" || answer === "yes");
|
|
6209
|
+
}
|
|
6210
|
+
}, [submitted, input]);
|
|
6211
|
+
if (submitted) {
|
|
6212
|
+
return null;
|
|
6213
|
+
}
|
|
6214
|
+
return React11.createElement(
|
|
6215
|
+
Box12,
|
|
6216
|
+
{ flexDirection: "column", marginY: 1 },
|
|
6217
|
+
React11.createElement(
|
|
6218
|
+
Box12,
|
|
6219
|
+
{ marginBottom: 1 },
|
|
6220
|
+
React11.createElement(
|
|
6221
|
+
Text11,
|
|
6222
|
+
{ bold: true, color: "yellow" },
|
|
6223
|
+
"\u26A0\uFE0F WARNING: This will uninstall Mimir from your system."
|
|
6224
|
+
)
|
|
6225
|
+
),
|
|
6226
|
+
React11.createElement(
|
|
6227
|
+
Box12,
|
|
6228
|
+
null,
|
|
6229
|
+
React11.createElement(Text11, null, "Are you sure you want to continue? (y/N): "),
|
|
6230
|
+
React11.createElement(TextInput2, {
|
|
6231
|
+
value: input,
|
|
6232
|
+
onChange: setInput,
|
|
6233
|
+
onSubmit: () => setSubmitted(true)
|
|
6234
|
+
})
|
|
6235
|
+
)
|
|
6236
|
+
);
|
|
6237
|
+
};
|
|
6238
|
+
render3(React11.createElement(ConfirmPrompt));
|
|
6239
|
+
});
|
|
6240
|
+
}
|
|
6241
|
+
async promptKeepConfig() {
|
|
6242
|
+
return new Promise((resolve) => {
|
|
6243
|
+
const ConfigPrompt = () => {
|
|
6244
|
+
const [input, setInput] = React11.useState("");
|
|
6245
|
+
const [submitted, setSubmitted] = React11.useState(false);
|
|
6246
|
+
React11.useEffect(() => {
|
|
6247
|
+
if (submitted) {
|
|
6248
|
+
const answer = input.toLowerCase();
|
|
6249
|
+
resolve(answer !== "n" && answer !== "no");
|
|
6250
|
+
}
|
|
6251
|
+
}, [submitted, input]);
|
|
6252
|
+
if (submitted) {
|
|
6253
|
+
return null;
|
|
6254
|
+
}
|
|
6255
|
+
return React11.createElement(
|
|
6256
|
+
Box12,
|
|
6257
|
+
{ flexDirection: "column", marginY: 1 },
|
|
6258
|
+
React11.createElement(
|
|
6259
|
+
Box12,
|
|
6260
|
+
{ marginBottom: 1 },
|
|
6261
|
+
React11.createElement(
|
|
6262
|
+
Text11,
|
|
6263
|
+
null,
|
|
6264
|
+
"Do you want to keep your Mimir configuration and data in ~/.mimir?"
|
|
6265
|
+
)
|
|
6266
|
+
),
|
|
6267
|
+
React11.createElement(
|
|
6268
|
+
Box12,
|
|
6269
|
+
null,
|
|
6270
|
+
React11.createElement(Text11, null, "Keep configuration? (Y/n): "),
|
|
6271
|
+
React11.createElement(TextInput2, {
|
|
6272
|
+
value: input,
|
|
6273
|
+
onChange: setInput,
|
|
6274
|
+
onSubmit: () => setSubmitted(true)
|
|
6275
|
+
})
|
|
6276
|
+
)
|
|
6277
|
+
);
|
|
6278
|
+
};
|
|
6279
|
+
render3(React11.createElement(ConfigPrompt));
|
|
6280
|
+
});
|
|
6281
|
+
}
|
|
6282
|
+
async uninstall(keepConfig, quiet = false) {
|
|
6283
|
+
const result = {
|
|
6284
|
+
success: true,
|
|
6285
|
+
removed: [],
|
|
6286
|
+
errors: [],
|
|
6287
|
+
keepConfig
|
|
6288
|
+
};
|
|
6289
|
+
let clear;
|
|
6290
|
+
if (!quiet) {
|
|
6291
|
+
const UninstallProgress = () => React11.createElement(
|
|
6292
|
+
Box12,
|
|
6293
|
+
null,
|
|
6294
|
+
React11.createElement(
|
|
6295
|
+
Text11,
|
|
6296
|
+
{ color: "cyan" },
|
|
6297
|
+
React11.createElement(Spinner, { type: "dots" }),
|
|
6298
|
+
" Uninstalling Mimir..."
|
|
6299
|
+
)
|
|
6300
|
+
);
|
|
6301
|
+
const rendered = render3(React11.createElement(UninstallProgress));
|
|
6302
|
+
clear = rendered.clear;
|
|
6303
|
+
}
|
|
6304
|
+
try {
|
|
6305
|
+
const homeDir = os6.homedir();
|
|
6306
|
+
const installType = await this.detectInstallType();
|
|
6307
|
+
if (!quiet) {
|
|
6308
|
+
logger.info(`Detected installation type: ${installType}`);
|
|
6309
|
+
}
|
|
6310
|
+
if (installType === "binary") {
|
|
6311
|
+
await this.removeBinaryInstallation(homeDir, result, quiet);
|
|
6312
|
+
}
|
|
6313
|
+
if (installType === "npm") {
|
|
6314
|
+
await this.removeNpmInstallation(result, quiet);
|
|
6315
|
+
}
|
|
6316
|
+
if (!keepConfig) {
|
|
6317
|
+
await this.removeGlobalConfig(homeDir, result, quiet);
|
|
6318
|
+
} else if (!quiet) {
|
|
6319
|
+
logger.info("Keeping global configuration at ~/.mimir");
|
|
6320
|
+
}
|
|
6321
|
+
} catch (error) {
|
|
6322
|
+
result.success = false;
|
|
6323
|
+
result.errors.push(
|
|
6324
|
+
`Uninstall failed: ${error instanceof Error ? error.message : String(error)}`
|
|
6325
|
+
);
|
|
6326
|
+
if (!quiet) {
|
|
6327
|
+
logger.error("Uninstall error", { error });
|
|
6328
|
+
}
|
|
6329
|
+
} finally {
|
|
6330
|
+
if (clear) {
|
|
6331
|
+
clear();
|
|
6332
|
+
}
|
|
6333
|
+
}
|
|
6334
|
+
return result;
|
|
6335
|
+
}
|
|
6336
|
+
async detectInstallType() {
|
|
6337
|
+
try {
|
|
6338
|
+
const scriptPath = process.argv[1];
|
|
6339
|
+
if (!scriptPath) {
|
|
6340
|
+
logger.debug("No script path found in process.argv[1]");
|
|
6341
|
+
return "unknown";
|
|
6342
|
+
}
|
|
6343
|
+
logger.debug(`Detecting install type from script path: ${scriptPath}`);
|
|
6344
|
+
const normalizedPath = path8.normalize(scriptPath).toLowerCase();
|
|
6345
|
+
if (normalizedPath.includes("node_modules")) {
|
|
6346
|
+
logger.debug("Detected npm installation (node_modules in path)");
|
|
6347
|
+
return "npm";
|
|
6348
|
+
}
|
|
6349
|
+
const homeDir = os6.homedir();
|
|
6350
|
+
const mimirBinPath = path8.normalize(path8.join(homeDir, ".mimir", "bin")).toLowerCase();
|
|
6351
|
+
const localBinPath = path8.normalize(path8.join(homeDir, ".local", "bin")).toLowerCase();
|
|
6352
|
+
if (normalizedPath.includes(mimirBinPath)) {
|
|
6353
|
+
logger.debug("Detected binary installation (.mimir/bin in path)");
|
|
6354
|
+
return "binary";
|
|
6355
|
+
}
|
|
6356
|
+
if (normalizedPath.includes(localBinPath)) {
|
|
6357
|
+
logger.debug("Detected binary installation (.local/bin in path)");
|
|
6358
|
+
return "binary";
|
|
6359
|
+
}
|
|
6360
|
+
const binaryPaths = [
|
|
6361
|
+
// Unix binary locations
|
|
6362
|
+
path8.join(homeDir, ".local", "bin", "mimir"),
|
|
6363
|
+
path8.join(homeDir, ".mimir", "bin", "mimir"),
|
|
6364
|
+
// Windows binary locations (.exe, .cmd variants)
|
|
6365
|
+
path8.join(homeDir, ".local", "bin", "mimir.exe"),
|
|
6366
|
+
path8.join(homeDir, ".local", "bin", "mimir.cmd"),
|
|
6367
|
+
path8.join(homeDir, ".mimir", "bin", "mimir.exe"),
|
|
6368
|
+
path8.join(homeDir, ".mimir", "bin", "mimir.cmd")
|
|
6369
|
+
];
|
|
6370
|
+
for (const binPath of binaryPaths) {
|
|
6371
|
+
if (await this.fs.exists(binPath)) {
|
|
6372
|
+
logger.debug(`Detected binary installation (found file at ${binPath})`);
|
|
6373
|
+
return "binary";
|
|
6374
|
+
}
|
|
6375
|
+
}
|
|
6376
|
+
logger.debug("Could not detect installation type - defaulting to unknown");
|
|
6377
|
+
return "unknown";
|
|
6378
|
+
} catch (error) {
|
|
6379
|
+
logger.debug("Error detecting installation type", { error });
|
|
6380
|
+
return "unknown";
|
|
6381
|
+
}
|
|
6382
|
+
}
|
|
6383
|
+
async removeNpmInstallation(result, quiet = false) {
|
|
6384
|
+
if (!this.executor) {
|
|
6385
|
+
if (!quiet) {
|
|
6386
|
+
logger.warn("Cannot automatically uninstall npm package.");
|
|
6387
|
+
logger.info("Please run manually: npm uninstall -g @codedir/mimir-code");
|
|
6388
|
+
}
|
|
6389
|
+
return;
|
|
6390
|
+
}
|
|
6391
|
+
try {
|
|
6392
|
+
if (!quiet) {
|
|
6393
|
+
logger.info("Removing npm global package...");
|
|
6394
|
+
}
|
|
6395
|
+
const npmResult = await this.executor.execute(
|
|
6396
|
+
"npm",
|
|
6397
|
+
["uninstall", "-g", "@codedir/mimir-code"],
|
|
6398
|
+
{
|
|
6399
|
+
cwd: process.cwd()
|
|
6400
|
+
}
|
|
6401
|
+
);
|
|
6402
|
+
if (npmResult.exitCode === 0) {
|
|
6403
|
+
result.removed.push("npm global package (@codedir/mimir-code)");
|
|
6404
|
+
if (!quiet) {
|
|
6405
|
+
logger.info("Successfully uninstalled npm package");
|
|
6406
|
+
}
|
|
6407
|
+
} else {
|
|
6408
|
+
throw new Error(`npm uninstall failed: ${npmResult.stderr}`);
|
|
6409
|
+
}
|
|
6410
|
+
} catch (error) {
|
|
6411
|
+
if (!quiet) {
|
|
6412
|
+
logger.error("Failed to uninstall npm package", { error });
|
|
6413
|
+
logger.info("Please run manually: npm uninstall -g @codedir/mimir-code");
|
|
6414
|
+
}
|
|
6415
|
+
result.errors.push(
|
|
6416
|
+
`npm uninstall failed: ${error instanceof Error ? error.message : String(error)}`
|
|
6417
|
+
);
|
|
6418
|
+
}
|
|
6419
|
+
}
|
|
6420
|
+
async removeBinaryInstallation(homeDir, result, quiet = false) {
|
|
6421
|
+
const isWindows = process.platform === "win32";
|
|
6422
|
+
const localBinPath = path8.join(homeDir, ".local", "bin", "mimir");
|
|
6423
|
+
const mimirBinDir = path8.join(homeDir, ".mimir", "bin");
|
|
6424
|
+
if (isWindows) {
|
|
6425
|
+
await this.spawnWindowsCleanup(localBinPath, mimirBinDir, quiet);
|
|
6426
|
+
await this.removeFromWindowsPath(quiet);
|
|
6427
|
+
result.removed.push("Binary (scheduled for deletion after exit)");
|
|
6428
|
+
result.removed.push("PATH entry");
|
|
6429
|
+
if (!quiet) {
|
|
6430
|
+
logger.info("\u2713 Scheduled binary deletion");
|
|
6431
|
+
logger.info("\u2713 Removed from PATH");
|
|
6432
|
+
logger.warn("\u26A0 Cleanup will complete in ~3 seconds");
|
|
6433
|
+
}
|
|
6434
|
+
} else {
|
|
6435
|
+
if (await this.fs.exists(localBinPath)) {
|
|
6436
|
+
await this.fs.unlink(localBinPath);
|
|
6437
|
+
result.removed.push("~/.local/bin/mimir");
|
|
6438
|
+
if (!quiet) {
|
|
6439
|
+
logger.info("Removed binary from ~/.local/bin");
|
|
6440
|
+
}
|
|
6441
|
+
}
|
|
6442
|
+
if (await this.fs.exists(mimirBinDir)) {
|
|
6443
|
+
await this.fs.rmdir(mimirBinDir, { recursive: true });
|
|
6444
|
+
result.removed.push("~/.mimir/bin/");
|
|
6445
|
+
if (!quiet) {
|
|
6446
|
+
logger.info("Removed binary directory");
|
|
6447
|
+
}
|
|
6448
|
+
}
|
|
6449
|
+
if (!quiet) {
|
|
6450
|
+
logger.info("\u2713 Binary uninstalled");
|
|
6451
|
+
logger.warn("\u26A0 Current terminal still has mimir cached");
|
|
6452
|
+
logger.info(" Run: hash -r (to clear shell cache)");
|
|
6453
|
+
}
|
|
6454
|
+
}
|
|
6455
|
+
}
|
|
6456
|
+
async spawnWindowsCleanup(binPath, binDir, quiet) {
|
|
6457
|
+
if (!this.executor) {
|
|
6458
|
+
if (!quiet) {
|
|
6459
|
+
logger.warn("Cannot spawn cleanup process - no executor available");
|
|
6460
|
+
}
|
|
6461
|
+
return;
|
|
6462
|
+
}
|
|
6463
|
+
try {
|
|
6464
|
+
const cleanupScript = `@echo off
|
|
6465
|
+
REM Wait for parent process to exit
|
|
6466
|
+
timeout /t 3 /nobreak >nul 2>&1
|
|
6467
|
+
|
|
6468
|
+
REM Delete binary if it exists
|
|
6469
|
+
if exist "${binPath}" (
|
|
6470
|
+
del /f /q "${binPath}" >nul 2>&1
|
|
6471
|
+
)
|
|
6472
|
+
if exist "${binPath}.exe" (
|
|
6473
|
+
del /f /q "${binPath}.exe" >nul 2>&1
|
|
6474
|
+
)
|
|
6475
|
+
if exist "${binPath}.cmd" (
|
|
6476
|
+
del /f /q "${binPath}.cmd" >nul 2>&1
|
|
6477
|
+
)
|
|
6478
|
+
|
|
6479
|
+
REM Delete binary directory
|
|
6480
|
+
if exist "${binDir}" (
|
|
6481
|
+
rmdir /s /q "${binDir}" >nul 2>&1
|
|
6482
|
+
)
|
|
6483
|
+
|
|
6484
|
+
REM Delete this cleanup script
|
|
6485
|
+
del /f /q "%~f0" >nul 2>&1
|
|
6486
|
+
`;
|
|
6487
|
+
const tempDir = os6.tmpdir();
|
|
6488
|
+
const cleanupPath = path8.join(tempDir, `mimir-cleanup-${Date.now()}.bat`);
|
|
6489
|
+
await this.fs.writeFile(cleanupPath, cleanupScript);
|
|
6490
|
+
const { spawn } = await import("child_process");
|
|
6491
|
+
const child = spawn("cmd", ["/c", cleanupPath], {
|
|
6492
|
+
detached: true,
|
|
6493
|
+
stdio: "ignore",
|
|
6494
|
+
windowsHide: true
|
|
6495
|
+
});
|
|
6496
|
+
child.unref();
|
|
6497
|
+
if (!quiet) {
|
|
6498
|
+
logger.info("Spawned background cleanup process");
|
|
6499
|
+
}
|
|
6500
|
+
} catch (error) {
|
|
6501
|
+
if (!quiet) {
|
|
6502
|
+
logger.error("Failed to spawn cleanup process", { error });
|
|
6503
|
+
}
|
|
6504
|
+
}
|
|
6505
|
+
}
|
|
6506
|
+
async removeFromWindowsPath(quiet) {
|
|
6507
|
+
if (!this.executor) return;
|
|
6508
|
+
try {
|
|
6509
|
+
const result = await this.executor.execute(
|
|
6510
|
+
"powershell",
|
|
6511
|
+
["-NoProfile", "-Command", '[Environment]::GetEnvironmentVariable("Path", "User")'],
|
|
6512
|
+
{ cwd: process.cwd() }
|
|
6513
|
+
);
|
|
6514
|
+
if (result.exitCode !== 0) {
|
|
6515
|
+
throw new Error("Failed to read PATH");
|
|
6516
|
+
}
|
|
6517
|
+
const currentPath = result.stdout.trim();
|
|
6518
|
+
const pathEntries = currentPath.split(";").filter(Boolean);
|
|
6519
|
+
const filteredEntries = pathEntries.filter((entry) => {
|
|
6520
|
+
const normalizedEntry = path8.normalize(entry.trim()).toLowerCase();
|
|
6521
|
+
return !normalizedEntry.includes("mimir");
|
|
6522
|
+
});
|
|
6523
|
+
if (filteredEntries.length < pathEntries.length) {
|
|
6524
|
+
const newPath = filteredEntries.join(";");
|
|
6525
|
+
await this.executor.execute(
|
|
6526
|
+
"powershell",
|
|
6527
|
+
[
|
|
6528
|
+
"-NoProfile",
|
|
6529
|
+
"-Command",
|
|
6530
|
+
`[Environment]::SetEnvironmentVariable("Path", "${newPath}", "User")`
|
|
6531
|
+
],
|
|
6532
|
+
{ cwd: process.cwd() }
|
|
6533
|
+
);
|
|
6534
|
+
const removedCount = pathEntries.length - filteredEntries.length;
|
|
6535
|
+
if (!quiet) {
|
|
6536
|
+
logger.info(`Removed ${removedCount} PATH entry/entries containing 'mimir'`);
|
|
6537
|
+
}
|
|
6538
|
+
}
|
|
6539
|
+
} catch (error) {
|
|
6540
|
+
if (!quiet) {
|
|
6541
|
+
logger.warn("Failed to remove from PATH", { error });
|
|
6542
|
+
logger.info("You may need to manually remove from PATH");
|
|
6543
|
+
}
|
|
6544
|
+
}
|
|
6545
|
+
}
|
|
6546
|
+
async removeGlobalConfig(homeDir, result, quiet = false) {
|
|
6547
|
+
const mimirDir = path8.join(homeDir, ".mimir");
|
|
6548
|
+
if (await this.fs.exists(mimirDir)) {
|
|
6549
|
+
await this.fs.rmdir(mimirDir, { recursive: true });
|
|
6550
|
+
result.removed.push("~/.mimir/");
|
|
6551
|
+
if (!quiet) {
|
|
6552
|
+
logger.info("Removed configuration directory: ~/.mimir");
|
|
6553
|
+
logger.info("All Mimir data has been deleted.");
|
|
6554
|
+
}
|
|
6555
|
+
}
|
|
6556
|
+
}
|
|
6557
|
+
/* eslint-disable no-console */
|
|
6558
|
+
printSummary(result) {
|
|
6559
|
+
console.log("");
|
|
6560
|
+
console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
6561
|
+
if (result.success) {
|
|
6562
|
+
console.log("\u2705 Mimir has been uninstalled");
|
|
6563
|
+
} else {
|
|
6564
|
+
console.log("\u274C Uninstall completed with errors");
|
|
6565
|
+
}
|
|
6566
|
+
console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
6567
|
+
if (result.removed.length > 0) {
|
|
6568
|
+
console.log("");
|
|
6569
|
+
console.log("Removed:");
|
|
6570
|
+
result.removed.forEach((item) => console.log(` - ${item}`));
|
|
6571
|
+
}
|
|
6572
|
+
if (result.keepConfig) {
|
|
6573
|
+
console.log("");
|
|
6574
|
+
console.log("Configuration preserved:");
|
|
6575
|
+
console.log(" - ~/.mimir/ (your settings and data)");
|
|
6576
|
+
console.log("");
|
|
6577
|
+
console.log("To remove it later, run:");
|
|
6578
|
+
console.log(" mimir uninstall --yes --remove-config");
|
|
6579
|
+
console.log(" or manually: rm -rf ~/.mimir");
|
|
6580
|
+
}
|
|
6581
|
+
if (result.errors.length > 0) {
|
|
6582
|
+
console.log("");
|
|
6583
|
+
console.log("Errors:");
|
|
6584
|
+
result.errors.forEach((error) => console.log(` - ${error}`));
|
|
6585
|
+
}
|
|
6586
|
+
console.log("");
|
|
6587
|
+
console.log("Thank you for using Mimir! \u{1F44B}");
|
|
6588
|
+
console.log("");
|
|
6589
|
+
}
|
|
6590
|
+
/* eslint-enable no-console */
|
|
6591
|
+
};
|
|
6592
|
+
|
|
6061
6593
|
// src/cli.ts
|
|
6062
6594
|
var fs3 = new FileSystemAdapter();
|
|
6595
|
+
var executor = new ProcessExecutorAdapter();
|
|
6063
6596
|
var configLoader = new ConfigLoader(fs3);
|
|
6064
6597
|
var firstRunDetector = new FirstRunDetector(fs3);
|
|
6065
6598
|
var setupCommand = new SetupCommand(configLoader);
|
|
6066
6599
|
var chatCommand = new ChatCommand(configLoader, firstRunDetector, setupCommand, fs3);
|
|
6067
6600
|
var initCommand = new InitCommand(fs3, configLoader);
|
|
6601
|
+
var uninstallCommand = new UninstallCommand(fs3, executor);
|
|
6068
6602
|
var program = new Command();
|
|
6069
6603
|
program.name("mimir").description("Platform-agnostic, BYOK AI coding agent CLI").version("0.1.0");
|
|
6070
6604
|
program.command("setup").description("Run setup wizard").action(async () => {
|
|
@@ -6079,6 +6613,12 @@ program.command("init").description("Initialize Mimir in current project").optio
|
|
|
6079
6613
|
await initCommand.execute(void 0, options);
|
|
6080
6614
|
process.exit(0);
|
|
6081
6615
|
});
|
|
6616
|
+
program.command("uninstall").description("Uninstall Mimir from your system").option("-y, --yes", "Skip confirmation prompts").option("--keep-config", "Keep configuration directory (~/.mimir)").option("--remove-config", "Remove configuration directory (~/.mimir)").option("-q, --quiet", "Suppress output (implies --yes)").action(
|
|
6617
|
+
async (options) => {
|
|
6618
|
+
await uninstallCommand.execute(options);
|
|
6619
|
+
process.exit(0);
|
|
6620
|
+
}
|
|
6621
|
+
);
|
|
6082
6622
|
var history = program.command("history").description("Manage conversation history");
|
|
6083
6623
|
history.command("list").description("List recent conversations").action(() => {
|
|
6084
6624
|
logger.warn("Listing conversations... (not implemented yet)");
|