create-snipara 1.3.22 → 1.3.24
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 +14 -0
- package/dist/index.js +279 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -197,6 +197,19 @@ Writes `.snipara/companion/doctor.json` and validates:
|
|
|
197
197
|
- Snipara Sandbox CLI presence
|
|
198
198
|
- environment files
|
|
199
199
|
- hosted MCP reachability when a live API key is available
|
|
200
|
+
- exposed Hosted MCP tool aliases (`snipara_*`, with `rlm_*` reported as legacy)
|
|
201
|
+
- client-specific loading gaps that the terminal cannot prove
|
|
202
|
+
|
|
203
|
+
For Claude-specific support, run:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
npx create-snipara doctor --client claude
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
This separates "Snipara config was written" from "Claude actually loaded the MCP
|
|
210
|
+
server." If Claude uses its cloud/UI MCP configuration instead of local files,
|
|
211
|
+
the doctor output points the user to Claude **Settings -> MCP** instead of
|
|
212
|
+
repeating local file edits.
|
|
200
213
|
|
|
201
214
|
### Repair local setup
|
|
202
215
|
|
|
@@ -269,6 +282,7 @@ That keeps business truth review separate from agent onboarding.
|
|
|
269
282
|
npx create-snipara --profile hosted-companion
|
|
270
283
|
npx create-snipara --profile hosted-only
|
|
271
284
|
npx create-snipara --profile full-stack
|
|
285
|
+
npx create-snipara --full
|
|
272
286
|
npx create-snipara --advanced
|
|
273
287
|
|
|
274
288
|
# Sandbox-only local execution
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import { homedir } from "os";
|
|
|
13
13
|
// package.json
|
|
14
14
|
var package_default = {
|
|
15
15
|
name: "create-snipara",
|
|
16
|
-
version: "1.3.
|
|
16
|
+
version: "1.3.24",
|
|
17
17
|
description: "Onboard Snipara Hosted MCP Context + Memory with auth, companion workflows, and optional Snipara Sandbox",
|
|
18
18
|
main: "dist/index.js",
|
|
19
19
|
type: "module",
|
|
@@ -170,11 +170,14 @@ function buildProgram() {
|
|
|
170
170
|
program.command("init").description("Install and configure Snipara for this project").option("--api-key <key>", "Snipara API key").option("--slug <slug>", "Project slug").option("--project-id <id>", "Project ID").option("--advanced", "Show advanced profile, project id, hooks, GitHub, and sandbox prompts").option(
|
|
171
171
|
"--profile <profile>",
|
|
172
172
|
"Install profile: hosted-only, hosted-companion, full-stack, runtime-only"
|
|
173
|
-
).option("--skip-companion", "Skip local companion CLI installation").option("--skip-mcp", "Skip local snipara-mcp package installation").option("--skip-runtime", "Skip snipara-sandbox installation").option("--skip-hooks", "Skip hook generation").option("--skip-test", "Skip hosted connection test").option("--with-orchestrator", "Install optional snipara-orchestrator package").option("--github", "Open GitHub repository automation setup when a git remote is detected").option("--skip-github", "Skip the GitHub automation prompt").option("--github-path <path>", "Optional GitHub path filter for the initial repository sync").option("--runtime-only", "Install only Snipara Sandbox").option("-y, --yes", "Accept defaults without prompting").action(async (options) => {
|
|
173
|
+
).option("--skip-companion", "Skip local companion CLI installation").option("--skip-mcp", "Skip local snipara-mcp package installation").option("--skip-runtime", "Skip snipara-sandbox installation").option("--skip-hooks", "Skip hook generation").option("--skip-test", "Skip hosted connection test").option("--with-orchestrator", "Install optional snipara-orchestrator package").option("--github", "Open GitHub repository automation setup when a git remote is detected").option("--skip-github", "Skip the GitHub automation prompt").option("--github-path <path>", "Optional GitHub path filter for the initial repository sync").option("--runtime-only", "Install only Snipara Sandbox").option("--full", "Alias for --profile full-stack").option("-y, --yes", "Accept defaults without prompting").action(async (options) => {
|
|
174
174
|
printBanner();
|
|
175
175
|
await runInit(options);
|
|
176
176
|
});
|
|
177
|
-
program.command("doctor").description("Inspect the current project and write a validation report").option(
|
|
177
|
+
program.command("doctor").description("Inspect the current project and write a validation report").option(
|
|
178
|
+
"--client <client>",
|
|
179
|
+
"Run client-specific diagnostics: claude, claude-code, claude-desktop, cursor, codex, etc."
|
|
180
|
+
).option("--skip-npm-check", "Skip npm latest-version freshness check").option("--json", "Print the report as JSON").action(async (options) => {
|
|
178
181
|
printBanner();
|
|
179
182
|
await runDoctor(options);
|
|
180
183
|
});
|
|
@@ -201,12 +204,20 @@ function addSharedRootOptions(program) {
|
|
|
201
204
|
program.option("--api-key <key>", "Snipara API key").option("--slug <slug>", "Project slug").option("--project-id <id>", "Project ID").option("--advanced", "Show advanced profile, project id, hooks, GitHub, and sandbox prompts").option(
|
|
202
205
|
"--profile <profile>",
|
|
203
206
|
"Install profile: hosted-only, hosted-companion, full-stack, runtime-only"
|
|
204
|
-
).option("--skip-companion", "Skip local companion CLI installation").option("--skip-mcp", "Skip local snipara-mcp package installation").option("--skip-runtime", "Skip snipara-sandbox installation").option("--skip-hooks", "Skip hook generation").option("--skip-test", "Skip hosted connection test").option("--with-orchestrator", "Install optional snipara-orchestrator package").option("--github", "Open GitHub repository automation setup when a git remote is detected").option("--skip-github", "Skip the GitHub automation prompt").option("--github-path <path>", "Optional GitHub path filter for the initial repository sync").option("--runtime-only", "Install only Snipara Sandbox").option("-y, --yes", "Accept defaults without prompting");
|
|
207
|
+
).option("--skip-companion", "Skip local companion CLI installation").option("--skip-mcp", "Skip local snipara-mcp package installation").option("--skip-runtime", "Skip snipara-sandbox installation").option("--skip-hooks", "Skip hook generation").option("--skip-test", "Skip hosted connection test").option("--with-orchestrator", "Install optional snipara-orchestrator package").option("--github", "Open GitHub repository automation setup when a git remote is detected").option("--skip-github", "Skip the GitHub automation prompt").option("--github-path <path>", "Optional GitHub path filter for the initial repository sync").option("--runtime-only", "Install only Snipara Sandbox").option("--full", "Alias for --profile full-stack").option("-y, --yes", "Accept defaults without prompting");
|
|
205
208
|
}
|
|
206
209
|
async function main() {
|
|
207
210
|
const program = buildProgram();
|
|
211
|
+
normalizeLegacyArgAliases(process.argv);
|
|
208
212
|
await program.parseAsync(process.argv);
|
|
209
213
|
}
|
|
214
|
+
function normalizeLegacyArgAliases(argv) {
|
|
215
|
+
for (let index = 2; index < argv.length; index += 1) {
|
|
216
|
+
if (argv[index] === "--Full") {
|
|
217
|
+
argv[index] = "--full";
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
210
221
|
function normalizeInitOptions(options) {
|
|
211
222
|
const parsed = parseInitOptionsFromArgv(process.argv.slice(2));
|
|
212
223
|
const normalized = { ...parsed };
|
|
@@ -235,6 +246,8 @@ function parseInitOptionsFromArgv(args) {
|
|
|
235
246
|
"--skip-test": "skipTest",
|
|
236
247
|
"--with-orchestrator": "withOrchestrator",
|
|
237
248
|
"--runtime-only": "runtimeOnly",
|
|
249
|
+
"--full": "full",
|
|
250
|
+
"--Full": "full",
|
|
238
251
|
"--github": "github",
|
|
239
252
|
"--skip-github": "skipGithub",
|
|
240
253
|
"--yes": "yes",
|
|
@@ -291,8 +304,16 @@ async function runInit(options) {
|
|
|
291
304
|
}
|
|
292
305
|
}
|
|
293
306
|
async function runDoctor(options) {
|
|
294
|
-
const
|
|
295
|
-
const
|
|
307
|
+
const requestedClient = normalizeClientOption(options.client);
|
|
308
|
+
const existingAnswers = loadExistingAnswers(options);
|
|
309
|
+
const answers = {
|
|
310
|
+
...existingAnswers,
|
|
311
|
+
aiClient: requestedClient || existingAnswers.aiClient
|
|
312
|
+
};
|
|
313
|
+
const report = await runValidation(answers, true, {
|
|
314
|
+
client: requestedClient || answers.aiClient,
|
|
315
|
+
includeNpmFreshness: !options.skipNpmCheck
|
|
316
|
+
});
|
|
296
317
|
writeDoctorReport(report);
|
|
297
318
|
if (options.json) {
|
|
298
319
|
console.log(JSON.stringify({ detection: detectEnvironment(), validation: report }, null, 2));
|
|
@@ -446,7 +467,9 @@ async function gatherConfiguration(options) {
|
|
|
446
467
|
skipRuntime: !!options.skipRuntime,
|
|
447
468
|
withOrchestrator: !!options.withOrchestrator,
|
|
448
469
|
skipHooks: !!options.skipHooks,
|
|
449
|
-
configureGitHub: Boolean(
|
|
470
|
+
configureGitHub: Boolean(
|
|
471
|
+
(options.github || options.githubPath) && detectedGitRepo && !options.skipGithub
|
|
472
|
+
),
|
|
450
473
|
githubRepo: detectedGitRepo || void 0,
|
|
451
474
|
githubPath: options.githubPath
|
|
452
475
|
});
|
|
@@ -563,7 +586,7 @@ async function gatherConfiguration(options) {
|
|
|
563
586
|
withOrchestrator: Boolean(options.withOrchestrator || response.installOrchestrator),
|
|
564
587
|
skipHooks: !!options.skipHooks || !response.installHooks,
|
|
565
588
|
configureGitHub: Boolean(
|
|
566
|
-
(response.configureGitHub || options.github) && detectedGitRepo && !options.skipGithub
|
|
589
|
+
(response.configureGitHub || options.github || options.githubPath) && detectedGitRepo && !options.skipGithub
|
|
567
590
|
),
|
|
568
591
|
githubRepo: detectedGitRepo || void 0,
|
|
569
592
|
githubPath: response.githubPath || options.githubPath
|
|
@@ -623,6 +646,9 @@ function resolveRequestedProfile(options) {
|
|
|
623
646
|
if ("runtimeOnly" in options && options.runtimeOnly) {
|
|
624
647
|
return "runtime-only";
|
|
625
648
|
}
|
|
649
|
+
if ("full" in options && options.full) {
|
|
650
|
+
return "full-stack";
|
|
651
|
+
}
|
|
626
652
|
if (options.profile && isInstallProfile(options.profile)) {
|
|
627
653
|
return options.profile;
|
|
628
654
|
}
|
|
@@ -631,6 +657,34 @@ function resolveRequestedProfile(options) {
|
|
|
631
657
|
function isInstallProfile(value) {
|
|
632
658
|
return ["hosted-only", "hosted-companion", "full-stack", "runtime-only"].includes(value);
|
|
633
659
|
}
|
|
660
|
+
function normalizeClientOption(value) {
|
|
661
|
+
if (!value) {
|
|
662
|
+
return null;
|
|
663
|
+
}
|
|
664
|
+
const normalized = value.trim().toLowerCase();
|
|
665
|
+
const aliases = {
|
|
666
|
+
claude: "claude-code",
|
|
667
|
+
"claude-code": "claude-code",
|
|
668
|
+
claude_code: "claude-code",
|
|
669
|
+
claudecli: "claude-code",
|
|
670
|
+
"claude-cli": "claude-code",
|
|
671
|
+
"claude-desktop": "claude-desktop",
|
|
672
|
+
claude_desktop: "claude-desktop",
|
|
673
|
+
cursor: "cursor",
|
|
674
|
+
windsurf: "windsurf",
|
|
675
|
+
codex: "codex",
|
|
676
|
+
gemini: "gemini",
|
|
677
|
+
chatgpt: "chatgpt",
|
|
678
|
+
vscode: "vscode",
|
|
679
|
+
continue: "continue",
|
|
680
|
+
other: "other"
|
|
681
|
+
};
|
|
682
|
+
const client = aliases[normalized];
|
|
683
|
+
if (!client) {
|
|
684
|
+
throw new Error(`Unknown client '${value}'. Use claude, claude-code, cursor, codex, or other.`);
|
|
685
|
+
}
|
|
686
|
+
return client;
|
|
687
|
+
}
|
|
634
688
|
function installProfileChoices() {
|
|
635
689
|
return [
|
|
636
690
|
{
|
|
@@ -2044,15 +2098,143 @@ function buildGitHubAutomationUrl(answers) {
|
|
|
2044
2098
|
}
|
|
2045
2099
|
return url.toString();
|
|
2046
2100
|
}
|
|
2047
|
-
|
|
2101
|
+
function getClaudeClientChecks(answers, detection) {
|
|
2102
|
+
const checks = [];
|
|
2103
|
+
const claudePaths = getClaudeConfigPaths();
|
|
2104
|
+
const projectConfig = readJsonFile(path.join(process.cwd(), ".mcp.json"));
|
|
2105
|
+
const codeSettings = readJsonFile(claudePaths.codeSettingsPath);
|
|
2106
|
+
const desktopConfig = readJsonFile(claudePaths.desktopConfigPath);
|
|
2107
|
+
const projectHasSnipara = containsSniparaMcpReference(projectConfig);
|
|
2108
|
+
const codeSettingsHasSnipara = containsSniparaMcpReference(codeSettings);
|
|
2109
|
+
const desktopHasSnipara = containsSniparaMcpReference(desktopConfig);
|
|
2110
|
+
checks.push({
|
|
2111
|
+
name: "claude_project_config",
|
|
2112
|
+
status: projectHasSnipara ? "pass" : "warn",
|
|
2113
|
+
detail: projectHasSnipara ? "Project .mcp.json contains a Snipara Hosted MCP server" : "Project .mcp.json does not contain a Snipara Hosted MCP server"
|
|
2114
|
+
});
|
|
2115
|
+
if (answers.aiClient === "claude-code") {
|
|
2116
|
+
checks.push({
|
|
2117
|
+
name: "claude_code_settings",
|
|
2118
|
+
status: detection.files.claudeSettings ? codeSettingsHasSnipara ? "pass" : "warn" : "warn",
|
|
2119
|
+
detail: detection.files.claudeSettings ? codeSettingsHasSnipara ? "~/.claude/settings.json references Snipara" : "~/.claude/settings.json exists but no Snipara MCP server was found" : "~/.claude/settings.json not found; Claude may require Settings -> MCP UI setup"
|
|
2120
|
+
});
|
|
2121
|
+
}
|
|
2122
|
+
if (answers.aiClient === "claude-desktop") {
|
|
2123
|
+
checks.push({
|
|
2124
|
+
name: "claude_desktop_config",
|
|
2125
|
+
status: detection.files.claudeDesktopConfig ? desktopHasSnipara ? "pass" : "warn" : "warn",
|
|
2126
|
+
detail: detection.files.claudeDesktopConfig ? desktopHasSnipara ? "Claude Desktop config references Snipara" : "Claude Desktop config exists but no Snipara MCP server was found" : "Claude Desktop config not found"
|
|
2127
|
+
});
|
|
2128
|
+
}
|
|
2129
|
+
checks.push({
|
|
2130
|
+
name: "claude_tools_visible",
|
|
2131
|
+
status: "warn",
|
|
2132
|
+
detail: "This terminal cannot prove Claude loaded the MCP server; confirm Snipara tools in Claude Settings -> MCP or ToolSearch after restart"
|
|
2133
|
+
});
|
|
2134
|
+
return checks;
|
|
2135
|
+
}
|
|
2136
|
+
function readJsonFile(filePath) {
|
|
2137
|
+
if (!fs.existsSync(filePath)) {
|
|
2138
|
+
return null;
|
|
2139
|
+
}
|
|
2140
|
+
try {
|
|
2141
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
2142
|
+
} catch {
|
|
2143
|
+
return null;
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
function containsSniparaMcpReference(value) {
|
|
2147
|
+
if (value === null || value === void 0) {
|
|
2148
|
+
return false;
|
|
2149
|
+
}
|
|
2150
|
+
if (typeof value === "string") {
|
|
2151
|
+
return value.includes("api.snipara.com/mcp") || value.includes("snipara-mcp");
|
|
2152
|
+
}
|
|
2153
|
+
if (Array.isArray(value)) {
|
|
2154
|
+
return value.some((item) => containsSniparaMcpReference(item));
|
|
2155
|
+
}
|
|
2156
|
+
if (typeof value === "object") {
|
|
2157
|
+
const record = value;
|
|
2158
|
+
const mcpServers = record.mcpServers;
|
|
2159
|
+
if (mcpServers && typeof mcpServers === "object" && "snipara" in mcpServers) {
|
|
2160
|
+
return true;
|
|
2161
|
+
}
|
|
2162
|
+
return Object.values(record).some((nested) => containsSniparaMcpReference(nested));
|
|
2163
|
+
}
|
|
2164
|
+
return false;
|
|
2165
|
+
}
|
|
2166
|
+
function getCreateSniparaPackageFreshnessCheck() {
|
|
2167
|
+
if (!commandExists("npm")) {
|
|
2168
|
+
return {
|
|
2169
|
+
name: "create_snipara_npm_version",
|
|
2170
|
+
status: "warn",
|
|
2171
|
+
detail: "npm not found; could not compare local create-snipara with npm latest"
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
try {
|
|
2175
|
+
const raw = execSync("npm view create-snipara version --json", {
|
|
2176
|
+
encoding: "utf-8",
|
|
2177
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
2178
|
+
shell: "/bin/sh",
|
|
2179
|
+
timeout: 4e3
|
|
2180
|
+
}).trim();
|
|
2181
|
+
const latest = raw.replace(/^"|"$/g, "");
|
|
2182
|
+
const comparison = compareSemver(VERSION, latest);
|
|
2183
|
+
if (comparison === 0) {
|
|
2184
|
+
return {
|
|
2185
|
+
name: "create_snipara_npm_version",
|
|
2186
|
+
status: "pass",
|
|
2187
|
+
detail: `Local package version ${VERSION} matches npm latest`
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
if (comparison > 0) {
|
|
2191
|
+
return {
|
|
2192
|
+
name: "create_snipara_npm_version",
|
|
2193
|
+
status: "warn",
|
|
2194
|
+
detail: `Local package version ${VERSION} is newer than npm latest ${latest}; publish may be pending`
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2197
|
+
return {
|
|
2198
|
+
name: "create_snipara_npm_version",
|
|
2199
|
+
status: "warn",
|
|
2200
|
+
detail: `npm latest is ${latest}; rerun with npx create-snipara@latest if this local copy is older`
|
|
2201
|
+
};
|
|
2202
|
+
} catch {
|
|
2203
|
+
return {
|
|
2204
|
+
name: "create_snipara_npm_version",
|
|
2205
|
+
status: "warn",
|
|
2206
|
+
detail: "Could not reach npm to compare create-snipara latest version"
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
function compareSemver(left, right) {
|
|
2211
|
+
const leftParts = left.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
2212
|
+
const rightParts = right.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
2213
|
+
const length = Math.max(leftParts.length, rightParts.length);
|
|
2214
|
+
for (let index = 0; index < length; index += 1) {
|
|
2215
|
+
const diff = (leftParts[index] || 0) - (rightParts[index] || 0);
|
|
2216
|
+
if (diff !== 0) {
|
|
2217
|
+
return diff > 0 ? 1 : -1;
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
return 0;
|
|
2221
|
+
}
|
|
2222
|
+
async function runValidation(answers, includeHostedCheck, options = {}) {
|
|
2048
2223
|
console.log(chalk.bold("\n\u{1FA7A} Running Validation\n"));
|
|
2049
2224
|
const checks = [];
|
|
2050
2225
|
const detection = detectEnvironment();
|
|
2226
|
+
const targetClient = options.client || answers.aiClient;
|
|
2227
|
+
const effectiveAnswers = { ...answers, aiClient: targetClient };
|
|
2051
2228
|
checks.push({
|
|
2052
2229
|
name: "project_slug",
|
|
2053
2230
|
status: answers.projectSlug ? "pass" : "fail",
|
|
2054
2231
|
detail: answers.projectSlug || "Project slug missing"
|
|
2055
2232
|
});
|
|
2233
|
+
checks.push({
|
|
2234
|
+
name: "target_client",
|
|
2235
|
+
status: "pass",
|
|
2236
|
+
detail: getClientDisplayName(targetClient)
|
|
2237
|
+
});
|
|
2056
2238
|
checks.push({
|
|
2057
2239
|
name: "mcp_config",
|
|
2058
2240
|
status: detection.files.mcpConfig ? "pass" : "warn",
|
|
@@ -2090,9 +2272,15 @@ async function runValidation(answers, includeHostedCheck) {
|
|
|
2090
2272
|
});
|
|
2091
2273
|
checks.push({
|
|
2092
2274
|
name: "hooks",
|
|
2093
|
-
status: answers.installHooks &&
|
|
2094
|
-
detail: answers.installHooks &&
|
|
2275
|
+
status: answers.installHooks && targetClient === "claude-code" ? detection.files.claudeDir ? "pass" : "warn" : "pass",
|
|
2276
|
+
detail: answers.installHooks && targetClient === "claude-code" ? detection.files.claudeDir ? "~/.claude detected" : "~/.claude missing" : "Hooks not requested"
|
|
2095
2277
|
});
|
|
2278
|
+
if (targetClient === "claude-code" || targetClient === "claude-desktop") {
|
|
2279
|
+
checks.push(...getClaudeClientChecks(effectiveAnswers, detection));
|
|
2280
|
+
}
|
|
2281
|
+
if (options.includeNpmFreshness) {
|
|
2282
|
+
checks.push(getCreateSniparaPackageFreshnessCheck());
|
|
2283
|
+
}
|
|
2096
2284
|
const githubRepo = answers.githubRepo || detectGitRepository();
|
|
2097
2285
|
checks.push({
|
|
2098
2286
|
name: "github_answer_packs",
|
|
@@ -2125,11 +2313,12 @@ function writeDoctorReport(report) {
|
|
|
2125
2313
|
console.log(chalk.green(" \u2713 Wrote .snipara/companion/doctor.json"));
|
|
2126
2314
|
}
|
|
2127
2315
|
function printDoctorReport(detection, report) {
|
|
2316
|
+
const targetClient = report.checks.find((check) => check.name === "target_client")?.detail || detection.aiClient;
|
|
2128
2317
|
console.log(chalk.bold("\n\u{1FA7A} Snipara Doctor\n"));
|
|
2129
2318
|
console.log(
|
|
2130
2319
|
` Project slug: ${detection.existingConfig.slug || detection.projectSlug || "unknown"}`
|
|
2131
2320
|
);
|
|
2132
|
-
console.log(` AI client: ${
|
|
2321
|
+
console.log(` AI client: ${targetClient}`);
|
|
2133
2322
|
console.log(` In git repo: ${detection.inGitRepo ? "yes" : "no"}`);
|
|
2134
2323
|
console.log(` Profile: ${report.profile}`);
|
|
2135
2324
|
console.log("");
|
|
@@ -2164,22 +2353,31 @@ async function testHostedConnection(answers) {
|
|
|
2164
2353
|
];
|
|
2165
2354
|
}
|
|
2166
2355
|
spinner.succeed("Connected to Snipara Hosted MCP.");
|
|
2356
|
+
const toolNames = extractMcpToolNames(toolsResponse.data);
|
|
2357
|
+
const hasSniparaTools = toolNames.some((name) => name.startsWith("snipara_"));
|
|
2358
|
+
const hasLegacyTools = toolNames.some((name) => name.startsWith("rlm_"));
|
|
2167
2359
|
const checks = [
|
|
2168
2360
|
{
|
|
2169
2361
|
name: "hosted_connection",
|
|
2170
2362
|
status: "pass",
|
|
2171
2363
|
detail: "tools/list succeeded"
|
|
2364
|
+
},
|
|
2365
|
+
{
|
|
2366
|
+
name: "hosted_tool_aliases",
|
|
2367
|
+
status: hasSniparaTools ? "pass" : hasLegacyTools ? "warn" : "warn",
|
|
2368
|
+
detail: hasSniparaTools ? "snipara_* tools are exposed by Hosted MCP" : hasLegacyTools ? "Only legacy rlm_* tool aliases were detected; Hosted MCP is usable but naming is stale" : "tools/list succeeded, but expected snipara_* tools were not detected"
|
|
2172
2369
|
}
|
|
2173
2370
|
];
|
|
2371
|
+
const settingsTool = hasSniparaTools ? "snipara_settings" : hasLegacyTools ? "rlm_settings" : "snipara_settings";
|
|
2174
2372
|
const settingsResponse = await callHostedMcp(answers, "tools/call", {
|
|
2175
|
-
name:
|
|
2373
|
+
name: settingsTool,
|
|
2176
2374
|
arguments: {}
|
|
2177
2375
|
});
|
|
2178
2376
|
if (settingsResponse.ok && !settingsResponse.data.error) {
|
|
2179
2377
|
checks.push({
|
|
2180
2378
|
name: "hosted_settings",
|
|
2181
2379
|
status: "pass",
|
|
2182
|
-
detail:
|
|
2380
|
+
detail: `${settingsTool} tool call succeeded`
|
|
2183
2381
|
});
|
|
2184
2382
|
} else {
|
|
2185
2383
|
checks.push({
|
|
@@ -2200,6 +2398,15 @@ async function testHostedConnection(answers) {
|
|
|
2200
2398
|
];
|
|
2201
2399
|
}
|
|
2202
2400
|
}
|
|
2401
|
+
function extractMcpToolNames(data) {
|
|
2402
|
+
const tools = data.result?.tools;
|
|
2403
|
+
if (!Array.isArray(tools)) {
|
|
2404
|
+
return [];
|
|
2405
|
+
}
|
|
2406
|
+
return tools.map(
|
|
2407
|
+
(tool) => tool && typeof tool === "object" && "name" in tool ? String(tool.name || "") : ""
|
|
2408
|
+
).filter(Boolean);
|
|
2409
|
+
}
|
|
2203
2410
|
async function callHostedMcp(answers, method, params) {
|
|
2204
2411
|
const response = await fetch(`${API_URL}/mcp/${answers.projectSlug}`, {
|
|
2205
2412
|
method: "POST",
|
|
@@ -2251,6 +2458,39 @@ function describeJsonRpcError(data) {
|
|
|
2251
2458
|
const code = data.error.code !== void 0 ? ` ${data.error.code}` : "";
|
|
2252
2459
|
return `JSON-RPC error${code}: ${data.error.message || "unknown error"}`;
|
|
2253
2460
|
}
|
|
2461
|
+
function getCheck(report, name) {
|
|
2462
|
+
return report.checks.find((check) => check.name === name);
|
|
2463
|
+
}
|
|
2464
|
+
function formatCheckStatus(check) {
|
|
2465
|
+
if (!check) {
|
|
2466
|
+
return "not checked";
|
|
2467
|
+
}
|
|
2468
|
+
if (check.status === "pass") {
|
|
2469
|
+
return `OK - ${check.detail}`;
|
|
2470
|
+
}
|
|
2471
|
+
if (check.status === "fail") {
|
|
2472
|
+
return `FAILED - ${check.detail}`;
|
|
2473
|
+
}
|
|
2474
|
+
return `NEEDS ATTENTION - ${check.detail}`;
|
|
2475
|
+
}
|
|
2476
|
+
function printSetupVerdict(answers, report) {
|
|
2477
|
+
const hostedConnection = getCheck(report, "hosted_connection");
|
|
2478
|
+
const toolAliases = getCheck(report, "hosted_tool_aliases");
|
|
2479
|
+
const claudeTools = getCheck(report, "claude_tools_visible");
|
|
2480
|
+
console.log(chalk.bold("\nSetup verdict:\n"));
|
|
2481
|
+
console.log(` Config written: ${answers.profile !== "runtime-only" ? "OK" : "not requested"}`);
|
|
2482
|
+
console.log(` Hosted MCP: ${formatCheckStatus(hostedConnection)}`);
|
|
2483
|
+
if (toolAliases) {
|
|
2484
|
+
console.log(` Tool aliases: ${formatCheckStatus(toolAliases)}`);
|
|
2485
|
+
}
|
|
2486
|
+
if (answers.aiClient === "claude-code" || answers.aiClient === "claude-desktop") {
|
|
2487
|
+
console.log(
|
|
2488
|
+
` Claude loaded MCP: ${claudeTools ? formatCheckStatus(claudeTools) : "not verified"}`
|
|
2489
|
+
);
|
|
2490
|
+
} else {
|
|
2491
|
+
console.log(" AI client loaded MCP: not verifiable from this terminal");
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2254
2494
|
function printSuccess(answers, report) {
|
|
2255
2495
|
console.log(
|
|
2256
2496
|
chalk.green(`
|
|
@@ -2263,6 +2503,7 @@ function printSuccess(answers, report) {
|
|
|
2263
2503
|
);
|
|
2264
2504
|
console.log(chalk.bold("Installed profile:\n"));
|
|
2265
2505
|
console.log(` ${chalk.green("\u2713")} ${answers.profile}`);
|
|
2506
|
+
printSetupVerdict(answers, report);
|
|
2266
2507
|
console.log(chalk.bold("\nWhat was installed:\n"));
|
|
2267
2508
|
if (answers.installMcp) {
|
|
2268
2509
|
console.log(
|
|
@@ -2300,6 +2541,20 @@ function printSuccess(answers, report) {
|
|
|
2300
2541
|
nextStep += 1;
|
|
2301
2542
|
};
|
|
2302
2543
|
printNextStep("Restart your AI client if you created or changed hosted MCP config.");
|
|
2544
|
+
if (answers.aiClient === "claude-code") {
|
|
2545
|
+
printNextStep(
|
|
2546
|
+
"For Claude Code, confirm Snipara appears in Settings -> MCP or ToolSearch; local files alone do not prove Claude loaded the server."
|
|
2547
|
+
);
|
|
2548
|
+
printNextStep("If Snipara tools are missing, run: npx create-snipara doctor --client claude");
|
|
2549
|
+
}
|
|
2550
|
+
if (answers.aiClient === "claude-desktop") {
|
|
2551
|
+
printNextStep(
|
|
2552
|
+
"For Claude Desktop, confirm the Snipara server appears in the desktop MCP settings after restart."
|
|
2553
|
+
);
|
|
2554
|
+
printNextStep(
|
|
2555
|
+
"If Snipara tools are missing, run: npx create-snipara doctor --client claude-desktop"
|
|
2556
|
+
);
|
|
2557
|
+
}
|
|
2303
2558
|
if (answers.aiClient === "codex") {
|
|
2304
2559
|
printNextStep(
|
|
2305
2560
|
"For Codex, merge .snipara/templates/codex-config.toml into ~/.codex/config.toml if the app does not read project .codex/config.toml."
|
|
@@ -2365,8 +2620,16 @@ function commandExists(command) {
|
|
|
2365
2620
|
return false;
|
|
2366
2621
|
}
|
|
2367
2622
|
}
|
|
2623
|
+
function getClaudeConfigPaths() {
|
|
2624
|
+
const desktopDir = process.platform === "darwin" ? path.join(homedir(), "Library", "Application Support", "Claude") : process.platform === "win32" ? path.join(homedir(), "AppData", "Roaming", "Claude") : path.join(homedir(), ".config", "claude");
|
|
2625
|
+
return {
|
|
2626
|
+
codeSettingsPath: path.join(homedir(), ".claude", "settings.json"),
|
|
2627
|
+
desktopConfigPath: path.join(desktopDir, "claude_desktop_config.json")
|
|
2628
|
+
};
|
|
2629
|
+
}
|
|
2368
2630
|
function detectEnvironment() {
|
|
2369
2631
|
const existingConfig = getExistingConfig();
|
|
2632
|
+
const claudePaths = getClaudeConfigPaths();
|
|
2370
2633
|
return {
|
|
2371
2634
|
cwd: process.cwd(),
|
|
2372
2635
|
projectSlug: detectProjectSlug(),
|
|
@@ -2382,6 +2645,8 @@ function detectEnvironment() {
|
|
|
2382
2645
|
codexConfig: fs.existsSync(path.join(process.cwd(), ".codex", "config.toml")),
|
|
2383
2646
|
cursorRule: fs.existsSync(path.join(process.cwd(), ".cursor", "rules", "snipara.mdc")),
|
|
2384
2647
|
claudeDir: fs.existsSync(path.join(homedir(), ".claude")),
|
|
2648
|
+
claudeSettings: fs.existsSync(claudePaths.codeSettingsPath),
|
|
2649
|
+
claudeDesktopConfig: fs.existsSync(claudePaths.desktopConfigPath),
|
|
2385
2650
|
cursorDir: fs.existsSync(path.join(homedir(), ".cursor")),
|
|
2386
2651
|
sniparaCompanionPack: fs.existsSync(path.join(process.cwd(), ".snipara", "companion")),
|
|
2387
2652
|
sniparaTemplates: fs.existsSync(path.join(process.cwd(), ".snipara", "templates"))
|
package/package.json
CHANGED