@mushi-mushi/cli 0.27.1 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/{chunk-M7CDSRZM.js → chunk-AV3RKAEE.js} +77 -47
- package/dist/{chunk-NNB2NONG.js → chunk-FJETUM25.js} +156 -497
- package/dist/chunk-KOBEOGNF.js +475 -0
- package/dist/chunk-SXSI2W6Z.js +42 -0
- package/dist/chunk-U2VGB63C.js +8 -0
- package/dist/chunk-UVO4A2G6.js +10 -0
- package/dist/connect-63GYZLFM.js +13 -0
- package/dist/{connect-5EQA7G3U.js → connect-C673EKS3.js} +74 -42
- package/dist/detect-4PYRZXVK.js +22 -0
- package/dist/endpoint-NHECZL47.js +15 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1202 -814
- package/dist/init.d.ts +179 -1
- package/dist/init.js +468 -225
- package/dist/{version-AYMUW3UQ.js → version-VR37VEE4.js} +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/wizard-args.d.ts +2 -2
- package/dist/wizard-args.js +5 -3
- package/package.json +10 -15
- package/CODE_OF_CONDUCT.md +0 -51
- package/CONTRIBUTING.md +0 -226
- package/SECURITY.md +0 -359
- package/dist/chunk-DBAEB56N.js +0 -10
- package/dist/chunk-XLGHUOKY.js +0 -8
- package/dist/connect-TWO2ZNFB.js +0 -10
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CONFIG_PATH,
|
|
3
|
-
|
|
3
|
+
describeUnsafeSdkKey,
|
|
4
|
+
probeKeyScope,
|
|
4
5
|
requireUuid,
|
|
5
6
|
saveConfig,
|
|
7
|
+
savedSdkKeyFor,
|
|
6
8
|
waitForIngestReady
|
|
7
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-AV3RKAEE.js";
|
|
8
10
|
import {
|
|
9
11
|
MUSHI_MCP_PIN_SPEC
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-U2VGB63C.js";
|
|
11
13
|
import {
|
|
12
14
|
detectFramework,
|
|
13
15
|
envVarsToWrite,
|
|
14
16
|
readPackageJson
|
|
15
17
|
} from "./chunk-AIZRI665.js";
|
|
18
|
+
import {
|
|
19
|
+
assertEndpoint
|
|
20
|
+
} from "./chunk-SXSI2W6Z.js";
|
|
16
21
|
import "./chunk-3RG5ZIWI.js";
|
|
17
22
|
|
|
18
23
|
// src/connect.ts
|
|
@@ -23,39 +28,62 @@ import { join, resolve } from "path";
|
|
|
23
28
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
24
29
|
import { dirname } from "path";
|
|
25
30
|
var DEFAULT_MUSHI_FEATURES = "triage,fixes,inventory,setup,docs";
|
|
31
|
+
function isPlainObject(value) {
|
|
32
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33
|
+
}
|
|
26
34
|
async function writeMcpServerEntry(opts) {
|
|
27
35
|
const { configPath, serverName, serverBlock, dryRun = false } = opts;
|
|
28
|
-
let
|
|
29
|
-
let created = false;
|
|
36
|
+
let existing = null;
|
|
30
37
|
try {
|
|
31
|
-
|
|
38
|
+
existing = await readFile(configPath, "utf8");
|
|
39
|
+
} catch {
|
|
40
|
+
existing = null;
|
|
41
|
+
}
|
|
42
|
+
let merged = {};
|
|
43
|
+
if (existing !== null && existing.trim() !== "") {
|
|
44
|
+
let parsed;
|
|
32
45
|
try {
|
|
33
|
-
|
|
46
|
+
parsed = JSON.parse(existing);
|
|
34
47
|
} catch {
|
|
35
|
-
|
|
48
|
+
throw new Error(`${configPath} is not valid JSON \u2014 fix or remove it, then re-run (nothing was written).`);
|
|
36
49
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
if (!isPlainObject(parsed)) {
|
|
51
|
+
throw new Error(`${configPath} is not a JSON object \u2014 fix or remove it, then re-run (nothing was written).`);
|
|
52
|
+
}
|
|
53
|
+
merged = parsed;
|
|
54
|
+
}
|
|
55
|
+
const currentServers = merged.mcpServers;
|
|
56
|
+
if (currentServers !== void 0 && !isPlainObject(currentServers)) {
|
|
57
|
+
throw new Error(`${configPath} has an "mcpServers" field that is not an object \u2014 fix it, then re-run (nothing was written).`);
|
|
40
58
|
}
|
|
41
|
-
|
|
42
|
-
servers[serverName] = serverBlock;
|
|
43
|
-
merged.mcpServers = servers;
|
|
59
|
+
merged.mcpServers = { ...currentServers ?? {}, [serverName]: serverBlock };
|
|
44
60
|
const output = JSON.stringify(merged, null, 2) + "\n";
|
|
45
61
|
if (!dryRun) {
|
|
46
62
|
await mkdir(dirname(configPath), { recursive: true });
|
|
47
63
|
await writeFile(configPath, output, "utf8");
|
|
48
64
|
}
|
|
49
|
-
return { created, path: configPath };
|
|
65
|
+
return { created: existing === null, path: configPath };
|
|
66
|
+
}
|
|
67
|
+
function apiKeyPlaceholderFor(client) {
|
|
68
|
+
switch (client) {
|
|
69
|
+
case "cursor":
|
|
70
|
+
return "${env:MUSHI_API_KEY}";
|
|
71
|
+
case "claude":
|
|
72
|
+
return "${MUSHI_API_KEY:-}";
|
|
73
|
+
case "continue":
|
|
74
|
+
case "zed":
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
50
77
|
}
|
|
51
78
|
function buildMcpServerBlock(opts) {
|
|
79
|
+
const keyValue = opts.inlineKey ? opts.apiKey : apiKeyPlaceholderFor(opts.client);
|
|
52
80
|
return {
|
|
53
81
|
command: "npx",
|
|
54
82
|
args: ["-y", MUSHI_MCP_PIN_SPEC],
|
|
55
83
|
env: {
|
|
56
84
|
MUSHI_API_ENDPOINT: opts.endpoint,
|
|
57
85
|
MUSHI_PROJECT_ID: opts.projectId,
|
|
58
|
-
|
|
86
|
+
...keyValue !== null ? { MUSHI_API_KEY: keyValue } : {},
|
|
59
87
|
MUSHI_FEATURES: DEFAULT_MUSHI_FEATURES
|
|
60
88
|
}
|
|
61
89
|
};
|
|
@@ -70,6 +98,12 @@ function buildMcpServerName(opts) {
|
|
|
70
98
|
}
|
|
71
99
|
|
|
72
100
|
// src/connect.ts
|
|
101
|
+
async function resolveSdkEnvKey(opts) {
|
|
102
|
+
const candidate = opts.sdkKey ?? savedSdkKeyFor(opts.baseConfig, opts.projectId) ?? opts.apiKey;
|
|
103
|
+
const probe = await opts.probe(opts.endpoint, candidate, opts.projectId);
|
|
104
|
+
if (probe.result === "ingest-only") return { ok: true, key: candidate };
|
|
105
|
+
return { ok: false, reason: describeUnsafeSdkKey(probe, opts.endpoint) };
|
|
106
|
+
}
|
|
73
107
|
function envKeyPresent(content, key) {
|
|
74
108
|
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
75
109
|
return new RegExp(`^${escaped}=`, "m").test(content);
|
|
@@ -99,43 +133,40 @@ ${needs.join("\n")}
|
|
|
99
133
|
await writeFile2(path, block, "utf8");
|
|
100
134
|
return true;
|
|
101
135
|
}
|
|
102
|
-
async function ensureMcpJsonGitignored(cwd, messages) {
|
|
103
|
-
const gitignorePath = join(cwd, ".gitignore");
|
|
104
|
-
const patterns = [".cursor/mcp.json", ".cursor/"];
|
|
105
|
-
let content = null;
|
|
106
|
-
try {
|
|
107
|
-
content = await readFile2(gitignorePath, "utf8");
|
|
108
|
-
} catch {
|
|
109
|
-
}
|
|
110
|
-
if (content === null) {
|
|
111
|
-
messages.push(
|
|
112
|
-
"\u26A0 No .gitignore found \u2014 .cursor/mcp.json contains your API key. Add `.cursor/mcp.json` before committing."
|
|
113
|
-
);
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
const covered = patterns.some((p) => content.split("\n").some((line) => line.trim() === p || line.trim() === `${p}/`));
|
|
117
|
-
if (covered) return;
|
|
118
|
-
await appendFile(gitignorePath, "\n# Mushi \u2014 keep MCP credentials out of git\n.cursor/mcp.json\n", "utf8");
|
|
119
|
-
messages.push("\u2713 Added .cursor/mcp.json to .gitignore (contains API key)");
|
|
120
|
-
}
|
|
121
136
|
async function runConnect(opts, baseConfig = {}) {
|
|
122
137
|
const cwd = resolve(opts.cwd ?? process.cwd());
|
|
123
138
|
const endpoint = assertEndpoint(opts.endpoint);
|
|
124
139
|
const projectId = requireUuid(opts.projectId, "projectId");
|
|
125
140
|
const messages = [];
|
|
141
|
+
const writeEnv = opts.writeEnv !== false;
|
|
142
|
+
const sdkEnvKey = writeEnv ? await resolveSdkEnvKey({
|
|
143
|
+
sdkKey: opts.sdkKey,
|
|
144
|
+
apiKey: opts.apiKey,
|
|
145
|
+
projectId,
|
|
146
|
+
endpoint,
|
|
147
|
+
baseConfig,
|
|
148
|
+
probe: opts.probeKeyScope ?? probeKeyScope
|
|
149
|
+
}) : null;
|
|
126
150
|
const config = {
|
|
127
151
|
...baseConfig,
|
|
128
152
|
apiKey: opts.apiKey,
|
|
129
153
|
projectId,
|
|
130
|
-
endpoint
|
|
154
|
+
endpoint,
|
|
155
|
+
...sdkEnvKey?.ok ? { sdkKey: { projectId, key: sdkEnvKey.key } } : {}
|
|
131
156
|
};
|
|
132
157
|
saveConfig(config);
|
|
133
158
|
messages.push(`\u2713 Credentials saved to ${CONFIG_PATH}`);
|
|
134
159
|
let envPath = null;
|
|
135
|
-
|
|
160
|
+
let envRefused = false;
|
|
161
|
+
if (sdkEnvKey && !sdkEnvKey.ok) {
|
|
162
|
+
envRefused = true;
|
|
163
|
+
messages.push(
|
|
164
|
+
`\u2717 Did not write SDK env vars: ${sdkEnvKey.reason}. Pass an ingest-only key with --sdk-key <key> (console \u2192 Projects \u2192 API keys, scope report:write), or run \`npx mushi-mushi\`, which mints one.`
|
|
165
|
+
);
|
|
166
|
+
} else if (sdkEnvKey?.ok) {
|
|
136
167
|
const pkg = readPackageJson(cwd);
|
|
137
168
|
const framework = detectFramework(cwd, pkg);
|
|
138
|
-
const lines = envVarsToWrite(
|
|
169
|
+
const lines = envVarsToWrite(sdkEnvKey.key, projectId, framework).split("\n");
|
|
139
170
|
envPath = join(cwd, ".env.local");
|
|
140
171
|
const wrote = await mergeEnvFile(envPath, lines);
|
|
141
172
|
messages.push(
|
|
@@ -157,13 +188,13 @@ async function runConnect(opts, baseConfig = {}) {
|
|
|
157
188
|
endpoint,
|
|
158
189
|
projectId,
|
|
159
190
|
apiKey: opts.apiKey,
|
|
191
|
+
client: "cursor",
|
|
160
192
|
inlineKey: false
|
|
161
|
-
//
|
|
193
|
+
// no secret in the file; the MCP server reads the saved CLI key
|
|
162
194
|
});
|
|
163
195
|
await writeMcpServerEntry({ configPath: mcpPath, serverName, serverBlock });
|
|
164
|
-
await ensureMcpJsonGitignored(cwd, messages);
|
|
165
196
|
messages.push(`\u2713 Wired ${mcpPath} \u2014 restart Cursor and run "list mushi tools"`);
|
|
166
|
-
messages.push(`
|
|
197
|
+
messages.push(` The MCP server reads the key saved in ${CONFIG_PATH} (export MUSHI_API_KEY before launching Cursor to override it).`);
|
|
167
198
|
}
|
|
168
199
|
let heartbeat = null;
|
|
169
200
|
if (opts.wait) {
|
|
@@ -197,9 +228,10 @@ async function runConnect(opts, baseConfig = {}) {
|
|
|
197
228
|
);
|
|
198
229
|
}
|
|
199
230
|
}
|
|
200
|
-
const ok = !opts.wait || Boolean(heartbeat?.ok);
|
|
231
|
+
const ok = !envRefused && (!opts.wait || Boolean(heartbeat?.ok));
|
|
201
232
|
return { ok, envPath, mcpPath, heartbeat, messages };
|
|
202
233
|
}
|
|
203
234
|
export {
|
|
235
|
+
resolveSdkEnvKey,
|
|
204
236
|
runConnect
|
|
205
237
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
FRAMEWORKS,
|
|
4
|
+
FRAMEWORK_IDS,
|
|
5
|
+
detectFramework,
|
|
6
|
+
detectPackageManager,
|
|
7
|
+
envVarsToWrite,
|
|
8
|
+
installCommand,
|
|
9
|
+
isFrameworkId,
|
|
10
|
+
readPackageJson
|
|
11
|
+
} from "./chunk-KOBEOGNF.js";
|
|
12
|
+
import "./chunk-PDX44BCA.js";
|
|
13
|
+
export {
|
|
14
|
+
FRAMEWORKS,
|
|
15
|
+
FRAMEWORK_IDS,
|
|
16
|
+
detectFramework,
|
|
17
|
+
detectPackageManager,
|
|
18
|
+
envVarsToWrite,
|
|
19
|
+
installCommand,
|
|
20
|
+
isFrameworkId,
|
|
21
|
+
readPackageJson
|
|
22
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLOUD_API_ENDPOINT,
|
|
3
|
+
TEST_REPORT_FETCH_TIMEOUT_MS,
|
|
4
|
+
assertEndpoint,
|
|
5
|
+
normalizeEndpoint,
|
|
6
|
+
resolveCloudEndpoint
|
|
7
|
+
} from "./chunk-SXSI2W6Z.js";
|
|
8
|
+
import "./chunk-3RG5ZIWI.js";
|
|
9
|
+
export {
|
|
10
|
+
CLOUD_API_ENDPOINT,
|
|
11
|
+
TEST_REPORT_FETCH_TIMEOUT_MS,
|
|
12
|
+
assertEndpoint,
|
|
13
|
+
normalizeEndpoint,
|
|
14
|
+
resolveCloudEndpoint
|
|
15
|
+
};
|
package/dist/index.d.ts
ADDED