create-snipara 1.3.40 → 1.3.41

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.
Files changed (3) hide show
  1. package/README.md +18 -4
  2. package/dist/index.js +377 -12
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -16,7 +16,7 @@ so an LLM client can use Snipara context and reviewed memory.
16
16
 
17
17
  Use it for:
18
18
 
19
- - Hosted MCP onboarding for Claude Code, Cursor, Windsurf, Codex, Gemini, VS Code/Copilot, Continue, ChatGPT Desktop, and similar clients
19
+ - Hosted MCP onboarding for Claude Code, Cursor, Windsurf, Codex, Gemini, Mistral, VS Code/Copilot, Continue, ChatGPT Desktop, and similar clients
20
20
  - free account signup with reviewed Context + Memory
21
21
  - local project wiring, `doctor`, `repair`, and `upgrade`
22
22
  - GitHub repository sync and PR Answer Packs approval when the current folder is a GitHub repo
@@ -33,7 +33,7 @@ flowchart LR
33
33
  CLI --> Auth["Snipara auth and project key"]
34
34
  CLI --> MCP["Hosted MCP config"]
35
35
  CLI --> Templates["AGENTS.md, CLAUDE.md, Cursor, Codex, MCP references"]
36
- MCP --> Agents["Claude Code, Cursor, Codex, Gemini, VS Code, ChatGPT"]
36
+ MCP --> Agents["Claude Code, Cursor, Codex, Gemini, Mistral, VS Code, ChatGPT"]
37
37
  Templates --> Agents
38
38
  Agents --> Context["Snipara context and memory"]
39
39
  ```
@@ -75,7 +75,7 @@ Free onboarding is now **Context + Memory**, not Context-only:
75
75
 
76
76
  - Context Free: three projects and 1,000 context queries/month
77
77
  - Memory Free: reviewed hosted memories for continuity across sessions
78
- - Hosted MCP: direct connection for Claude Code, Cursor, Windsurf, Codex, Gemini, VS Code/Copilot, Continue, ChatGPT Desktop, and other MCP clients
78
+ - Hosted MCP: direct connection for Claude Code, Cursor, Windsurf, Codex, Gemini, Mistral, VS Code/Copilot, Continue, ChatGPT Desktop, and other MCP clients
79
79
 
80
80
  The CLI opens a browser device-flow login when the user does not already have a key, auto-provisions the free Context + Memory account when needed, then writes the returned API key into the local MCP config.
81
81
 
@@ -108,6 +108,9 @@ That distinction matters:
108
108
  - generates merge-ready AI agent templates in `.snipara/templates/`, including Pro+ symbol-card, code-impact, and managed workflow guidance for paid Context plans
109
109
  - creates `AGENTS.md` and `CLAUDE.md` when missing, or appends a marked Snipara section when those files already exist
110
110
  - creates `.cursor/rules/snipara.mdc` and `.codex/config.toml` when those files do not already exist
111
+ - validates Codex config against Hosted MCP plus `bearer_token_env_var = "SNIPARA_API_KEY"` and tells users to restart Codex after MCP config changes
112
+ - runs a real hosted MCP `tools/list` check and warns if the server still advertises legacy `rlm_*` duplicate tools
113
+ - generates Mistral Le Chat, Mistral Vibe, and LangChain `ChatMistralAI.bindTools` references without embedding secrets
111
114
  - writes client-specific companion packs such as `.snipara/companion/CODEX.md` or `.snipara/companion/GEMINI.md`
112
115
  - updates `.env`, `.env.local`, and `.env.example`
113
116
  - generates `.snipara/companion/README.md`
@@ -161,8 +164,16 @@ The generated agent templates cover:
161
164
  - Cursor `.cursor/rules/snipara.mdc`
162
165
  - Codex `.codex/config.toml` snippet
163
166
  - ChatGPT/OpenAI HTTP MCP snippet
167
+ - Mistral Le Chat connector, Vibe config, and LangChain `ChatMistralAI.bindTools` snippets
164
168
  - Gemini, VS Code, Continue, and custom HTTP MCP reference snippets
165
169
 
170
+ For Codex, `SNIPARA_API_KEY` is the only generated bearer token environment
171
+ variable. If Codex lazily exposes only a minimal Snipara MCP surface after
172
+ restart, force exact discovery for `snipara_recall`, `snipara_context_query`,
173
+ and `snipara_settings`. Use `snipara-companion recall`, `snipara-companion
174
+ query`, and `snipara-companion task-commit` as the reliable local fallback while
175
+ the Codex session reloads or rediscovers tools.
176
+
166
177
  Existing root `AGENTS.md` and `CLAUDE.md` files are not replaced. The installer
167
178
  adds or refreshes a marked Snipara section so project-specific instructions stay
168
179
  intact.
@@ -344,8 +355,11 @@ companion add workflow ergonomics:
344
355
 
345
356
  Claude Code, Cursor, and Windsurf can receive generated automation files when
346
357
  that client has a useful local hook surface. Codex, Gemini, VS Code, Continue,
347
- ChatGPT, and custom clients are treated as MCP-first: they get the hosted MCP
358
+ Mistral, ChatGPT, and custom clients are treated as MCP-first: they get the hosted MCP
348
359
  configuration/reference and agent instructions, not invented local hooks.
360
+ For Mistral, request hooks from LangChain are documented as `ChatMistralAI`
361
+ request lifecycle hooks (`beforeRequestHooks`, `requestErrorHooks`, and
362
+ `responseHooks`), not as local agent lifecycle hooks.
349
363
 
350
364
  OpenClaw is the specialized exception: use `snipara-openclaw-install` for a
351
365
  single OpenClaw-focused setup command, or use `create-snipara` plus
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.40",
16
+ version: "1.3.41",
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",
@@ -47,6 +47,7 @@ var package_default = {
47
47
  "cursor",
48
48
  "windsurf",
49
49
  "gemini",
50
+ "mistral",
50
51
  "vscode",
51
52
  "continue",
52
53
  "claude-desktop"
@@ -128,6 +129,11 @@ var AI_CLIENT_CHOICES = [
128
129
  value: "gemini",
129
130
  description: "Hosted MCP reference plus agent instructions"
130
131
  },
132
+ {
133
+ title: "Mistral Le Chat / Vibe",
134
+ value: "mistral",
135
+ description: "Le Chat custom MCP connector plus Mistral Vibe config"
136
+ },
131
137
  {
132
138
  title: "VS Code / Copilot",
133
139
  value: "vscode",
@@ -157,6 +163,7 @@ function getCompanionInitClientFlag(aiClient) {
157
163
  windsurf: "windsurf",
158
164
  codex: "codex",
159
165
  gemini: "gemini",
166
+ mistral: "mistral",
160
167
  chatgpt: "chatgpt",
161
168
  vscode: "vscode",
162
169
  continue: "continue",
@@ -176,7 +183,7 @@ function buildProgram() {
176
183
  program.name("create-snipara").description("Scaffold hosted Snipara MCP config plus companion workflow helpers").version(VERSION);
177
184
  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(
178
185
  "--client <client>",
179
- "AI client: claude-code, cursor, windsurf, codex, gemini, chatgpt, vscode, continue, claude-desktop, other"
186
+ "AI client: claude-code, cursor, windsurf, codex, gemini, mistral, chatgpt, vscode, continue, claude-desktop, other"
180
187
  ).option("--advanced", "Show advanced profile, project id, hooks, GitHub, and sandbox prompts").option(
181
188
  "--profile <profile>",
182
189
  "Install profile: hosted-only, hosted-companion, full-stack, runtime-only"
@@ -213,7 +220,7 @@ function buildProgram() {
213
220
  function addSharedRootOptions(program) {
214
221
  program.option("--api-key <key>", "Snipara API key").option("--slug <slug>", "Project slug").option("--project-id <id>", "Project ID").option(
215
222
  "--client <client>",
216
- "AI client: claude-code, cursor, windsurf, codex, gemini, chatgpt, vscode, continue, claude-desktop, other"
223
+ "AI client: claude-code, cursor, windsurf, codex, gemini, mistral, chatgpt, vscode, continue, claude-desktop, other"
217
224
  ).option("--advanced", "Show advanced profile, project id, hooks, GitHub, and sandbox prompts").option(
218
225
  "--profile <profile>",
219
226
  "Install profile: hosted-only, hosted-companion, full-stack, runtime-only"
@@ -722,6 +729,12 @@ function normalizeClientOption(value) {
722
729
  windsurf: "windsurf",
723
730
  codex: "codex",
724
731
  gemini: "gemini",
732
+ mistral: "mistral",
733
+ "le-chat": "mistral",
734
+ lechat: "mistral",
735
+ vibe: "mistral",
736
+ "mistral-vibe": "mistral",
737
+ mistral_vibe: "mistral",
725
738
  chatgpt: "chatgpt",
726
739
  vscode: "vscode",
727
740
  continue: "continue",
@@ -730,7 +743,9 @@ function normalizeClientOption(value) {
730
743
  };
731
744
  const client = aliases[normalized];
732
745
  if (!client) {
733
- throw new Error(`Unknown client '${value}'. Use claude, claude-code, cursor, codex, or other.`);
746
+ throw new Error(
747
+ `Unknown client '${value}'. Use claude, claude-code, cursor, codex, mistral, or other.`
748
+ );
734
749
  }
735
750
  return client;
736
751
  }
@@ -1234,6 +1249,18 @@ async function createAgentTemplates(answers) {
1234
1249
  path: path.join(templatesDir, "gemini-mcp-reference.json"),
1235
1250
  content: buildGenericMcpReferenceTemplate(answers, "gemini")
1236
1251
  },
1252
+ {
1253
+ path: path.join(templatesDir, "mistral-vibe-config.toml"),
1254
+ content: buildMistralVibeConfigTemplate(answers)
1255
+ },
1256
+ {
1257
+ path: path.join(templatesDir, "mistral-le-chat-mcp.json"),
1258
+ content: buildMistralLeChatTemplate(answers)
1259
+ },
1260
+ {
1261
+ path: path.join(templatesDir, "mistral-langchain-tools.ts"),
1262
+ content: buildMistralLangChainToolsTemplate(answers)
1263
+ },
1237
1264
  {
1238
1265
  path: path.join(templatesDir, "vscode-mcp-reference.json"),
1239
1266
  content: buildGenericMcpReferenceTemplate(answers, "vscode")
@@ -1275,6 +1302,15 @@ async function createAgentTemplates(answers) {
1275
1302
  buildCodexConfigTemplate(answers),
1276
1303
  ".codex/config.toml"
1277
1304
  );
1305
+ if (answers.aiClient === "mistral") {
1306
+ const vibeDir = path.join(process.cwd(), ".vibe");
1307
+ fs.mkdirSync(vibeDir, { recursive: true });
1308
+ writeIfMissing(
1309
+ path.join(vibeDir, "config.toml"),
1310
+ buildMistralVibeConfigTemplate(answers),
1311
+ ".vibe/config.toml"
1312
+ );
1313
+ }
1278
1314
  ensureGitignoreEntry(".snipara/companion/doctor.json", "# Snipara generated validation output");
1279
1315
  }
1280
1316
  function writeIfMissing(filePath, content, label) {
@@ -1303,10 +1339,17 @@ This project uses Snipara Hosted MCP for project context and reviewed memory.
1303
1339
  call such as \`snipara_settings\`, \`tools/list\`, or a lightweight
1304
1340
  \`snipara_context_query\`. Do not treat empty MCP resources/templates as an
1305
1341
  outage because Snipara may be tool-only.
1342
+ - In Codex, if only a minimal Snipara tool subset appears, force precise tool
1343
+ discovery for \`snipara_recall\`, \`snipara_context_query\`, and
1344
+ \`snipara_settings\` before concluding the MCP server is incomplete.
1306
1345
  - If a session appears to expose only a subset of expected Snipara tools, call
1307
1346
  \`snipara_help(list_all=true)\` and compare exact tool names before concluding
1308
1347
  that a tool is unavailable. Do not infer absence from one broad discovery
1309
1348
  pass or a ranked tool-search result.
1349
+ - If Codex still exposes only a partial surface after exact discovery, use
1350
+ \`snipara-companion recall\`, \`snipara-companion query\`, and
1351
+ \`snipara-companion task-commit\` as the reliable local fallback, then restart
1352
+ or reload Codex after MCP config changes.
1310
1353
 
1311
1354
  Agent task lifecycle:
1312
1355
 
@@ -1430,6 +1473,138 @@ function buildChatGptMcpTemplate(answers) {
1430
1473
  )}
1431
1474
  `;
1432
1475
  }
1476
+ function buildMistralVibeConfigTemplate(answers) {
1477
+ return `[[mcp_servers]]
1478
+ name = "snipara"
1479
+ transport = "streamable-http"
1480
+ url = "${buildHostedMcpEndpoint(answers)}"
1481
+ api_key_env = "SNIPARA_API_KEY"
1482
+ api_key_header = "X-API-Key"
1483
+ api_key_format = "{token}"
1484
+ `;
1485
+ }
1486
+ function buildMistralLeChatTemplate(answers) {
1487
+ return `${JSON.stringify(
1488
+ {
1489
+ connector_name: "snipara",
1490
+ server_url: buildHostedMcpEndpoint(answers),
1491
+ auth: {
1492
+ method: "HTTP Bearer Token or Basic/API key prompt",
1493
+ secret: "Use SNIPARA_API_KEY from your environment or secret store"
1494
+ },
1495
+ notes: [
1496
+ "In Le Chat, add a Custom MCP Connector with this Server URL.",
1497
+ "Le Chat auto-detects the authentication flow; do not commit the API key.",
1498
+ "Enable the connector tools in each conversation or pre-authorize read functions deliberately."
1499
+ ]
1500
+ },
1501
+ null,
1502
+ 2
1503
+ )}
1504
+ `;
1505
+ }
1506
+ function buildMistralLangChainToolsTemplate(answers) {
1507
+ return `import { ChatMistralAI } from "@langchain/mistralai";
1508
+ import { tool } from "@langchain/core/tools";
1509
+ import { z } from "zod";
1510
+
1511
+ const SNIPARA_MCP_URL = process.env.SNIPARA_MCP_URL || "${buildHostedMcpEndpoint(answers)}";
1512
+ const SNIPARA_API_KEY = process.env.SNIPARA_API_KEY;
1513
+
1514
+ if (!SNIPARA_API_KEY) {
1515
+ throw new Error("Missing SNIPARA_API_KEY");
1516
+ }
1517
+
1518
+ async function callSniparaTool(toolName: string, args: Record<string, unknown>): Promise<string> {
1519
+ const response = await fetch(SNIPARA_MCP_URL, {
1520
+ method: "POST",
1521
+ headers: {
1522
+ "Content-Type": "application/json",
1523
+ "X-API-Key": SNIPARA_API_KEY,
1524
+ },
1525
+ body: JSON.stringify({
1526
+ jsonrpc: "2.0",
1527
+ id: Date.now(),
1528
+ method: "tools/call",
1529
+ params: {
1530
+ name: toolName,
1531
+ arguments: args,
1532
+ },
1533
+ }),
1534
+ });
1535
+
1536
+ const payload = (await response.json()) as { error?: unknown; result?: unknown };
1537
+ if (!response.ok || payload.error) {
1538
+ throw new Error("Snipara MCP call failed: " + JSON.stringify(payload.error || payload));
1539
+ }
1540
+
1541
+ return JSON.stringify(payload.result || {});
1542
+ }
1543
+
1544
+ export const sniparaRecallTool = tool(
1545
+ async ({ query, type }) =>
1546
+ callSniparaTool("snipara_recall", {
1547
+ query,
1548
+ ...(type ? { type } : {}),
1549
+ }),
1550
+ {
1551
+ name: "snipara_recall",
1552
+ description: "Recall durable Snipara decisions, preferences, and validated learnings.",
1553
+ schema: z.object({
1554
+ query: z.string().describe("Focused memory query."),
1555
+ type: z.enum(["decision", "learning", "context", "preference"]).optional(),
1556
+ }),
1557
+ }
1558
+ );
1559
+
1560
+ export const sniparaContextQueryTool = tool(
1561
+ async ({ query, maxTokens }) =>
1562
+ callSniparaTool("snipara_context_query", {
1563
+ query,
1564
+ max_tokens: maxTokens,
1565
+ return_references: true,
1566
+ }),
1567
+ {
1568
+ name: "snipara_context_query",
1569
+ description: "Retrieve optimized source context, docs, runbooks, and source-truth references.",
1570
+ schema: z.object({
1571
+ query: z.string().describe("Question or task needing project context."),
1572
+ maxTokens: z.number().int().positive().max(12000).default(4000),
1573
+ }),
1574
+ }
1575
+ );
1576
+
1577
+ export const sniparaSettingsTool = tool(() => callSniparaTool("snipara_settings", {}), {
1578
+ name: "snipara_settings",
1579
+ description: "Validate the hosted Snipara MCP server and project settings.",
1580
+ schema: z.object({}),
1581
+ });
1582
+
1583
+ const beforeRequestHook = (req: Request): Request => {
1584
+ const headers = new Headers(req.headers);
1585
+ headers.set("X-Snipara-Client", "mistral-langchain");
1586
+ return new Request(req, { headers });
1587
+ };
1588
+
1589
+ const requestErrorHook = async (err: unknown, req: Request): Promise<void> => {
1590
+ console.warn("Mistral request failed", { url: req.url, err });
1591
+ };
1592
+
1593
+ const responseHook = async (res: Response, req: Request): Promise<void> => {
1594
+ void req;
1595
+ void res;
1596
+ };
1597
+
1598
+ export const mistralWithSniparaTools = new ChatMistralAI({
1599
+ model: process.env.MISTRAL_MODEL || "mistral-large-latest",
1600
+ temperature: 0,
1601
+ maxRetries: 2,
1602
+ beforeRequestHooks: [beforeRequestHook],
1603
+ requestErrorHooks: [requestErrorHook],
1604
+ responseHooks: [responseHook],
1605
+ }).bindTools([sniparaRecallTool, sniparaContextQueryTool, sniparaSettingsTool]);
1606
+ `;
1607
+ }
1433
1608
  function buildGenericMcpReferenceTemplate(answers, client) {
1434
1609
  return `${JSON.stringify(
1435
1610
  {
@@ -1851,7 +2026,9 @@ function getClientGuidance(client) {
1851
2026
  if (client === "codex") {
1852
2027
  return [
1853
2028
  "Codex should use the hosted MCP surface plus AGENTS.md as the canonical project instruction file.",
1854
- "The generated .snipara/templates/codex-config.toml is a merge-ready snippet for ~/.codex/config.toml if your Codex app does not read project .codex/config.toml."
2029
+ "The generated .snipara/templates/codex-config.toml is a merge-ready snippet for ~/.codex/config.toml if your Codex app does not read project .codex/config.toml.",
2030
+ "Use SNIPARA_API_KEY as the only Codex bearer_token_env_var; restart Codex after changing MCP config.",
2031
+ "If Codex lazily exposes only a minimal tool set, force discovery for snipara_recall, snipara_context_query, and snipara_settings; use snipara-companion as the fallback until Codex reloads."
1855
2032
  ];
1856
2033
  }
1857
2034
  if (client === "gemini") {
@@ -1860,6 +2037,14 @@ function getClientGuidance(client) {
1860
2037
  "Use .snipara/templates/gemini-mcp-reference.json as a field reference for the client's MCP settings."
1861
2038
  ];
1862
2039
  }
2040
+ if (client === "mistral") {
2041
+ return [
2042
+ "Mistral Le Chat should use a Custom MCP Connector pointed at the hosted Snipara MCP endpoint.",
2043
+ "Mistral Vibe should use .vibe/config.toml or ~/.vibe/config.toml with the generated streamable-http server block and AGENTS.md at the workspace root.",
2044
+ "LangChain JavaScript can bind Snipara tools to ChatMistralAI with .snipara/templates/mistral-langchain-tools.ts.",
2045
+ "Native lifecycle hooks are not claimed for Mistral; use snipara-companion for explicit recall, query, workflow, and task-commit flows."
2046
+ ];
2047
+ }
1863
2048
  if (client === "vscode") {
1864
2049
  return [
1865
2050
  "VS Code and Copilot-style clients should use Hosted MCP plus AGENTS.md for project workflow rules.",
@@ -2026,6 +2211,7 @@ function getClientDisplayName(client) {
2026
2211
  windsurf: "Windsurf",
2027
2212
  codex: "OpenAI Codex",
2028
2213
  gemini: "Gemini",
2214
+ mistral: "Mistral Le Chat / Vibe",
2029
2215
  chatgpt: "ChatGPT / OpenAI MCP",
2030
2216
  vscode: "VS Code",
2031
2217
  continue: "Continue",
@@ -2041,6 +2227,7 @@ function getClientPackFilename(client) {
2041
2227
  windsurf: "WINDSURF.md",
2042
2228
  codex: "CODEX.md",
2043
2229
  gemini: "GEMINI.md",
2230
+ mistral: "MISTRAL.md",
2044
2231
  chatgpt: "CHATGPT.md",
2045
2232
  vscode: "VSCODE.md",
2046
2233
  continue: "CONTINUE.md",
@@ -2065,6 +2252,15 @@ function getClientConfigArtifacts(client) {
2065
2252
  if (client === "gemini") {
2066
2253
  return ["AGENTS.md", ".snipara/templates/gemini-mcp-reference.json"];
2067
2254
  }
2255
+ if (client === "mistral") {
2256
+ return [
2257
+ "AGENTS.md",
2258
+ ".vibe/config.toml",
2259
+ ".snipara/templates/mistral-vibe-config.toml",
2260
+ ".snipara/templates/mistral-le-chat-mcp.json",
2261
+ ".snipara/templates/mistral-langchain-tools.ts"
2262
+ ];
2263
+ }
2068
2264
  if (client === "vscode") {
2069
2265
  return ["AGENTS.md", ".snipara/templates/vscode-mcp-reference.json"];
2070
2266
  }
@@ -2502,6 +2698,12 @@ async function runValidation(answers, includeHostedCheck, options = {}) {
2502
2698
  status: detection.files.sniparaTemplates && detection.files.cursorRule && detection.files.codexConfig ? "pass" : "warn",
2503
2699
  detail: detection.files.sniparaTemplates && detection.files.cursorRule && detection.files.codexConfig ? ".snipara/templates, .cursor/rules/snipara.mdc, and .codex/config.toml present" : "Agent templates, Cursor rule, or Codex config missing"
2504
2700
  });
2701
+ if (targetClient === "codex") {
2702
+ checks.push(getCodexConfigCheck());
2703
+ }
2704
+ if (targetClient === "mistral") {
2705
+ checks.push(getMistralConfigCheck());
2706
+ }
2505
2707
  checks.push({
2506
2708
  name: "env_files",
2507
2709
  status: detection.files.env || detection.files.envLocal || detection.files.envExample ? "pass" : "warn",
@@ -2549,6 +2751,117 @@ function writeDoctorReport(report) {
2549
2751
  fs.writeFileSync(path.join(reportDir, "doctor.json"), JSON.stringify(report, null, 2) + "\n");
2550
2752
  console.log(chalk.green(" \u2713 Wrote .snipara/companion/doctor.json"));
2551
2753
  }
2754
+ function getCodexConfigCheck() {
2755
+ const candidates = [
2756
+ {
2757
+ label: ".codex/config.toml",
2758
+ filePath: path.join(process.cwd(), ".codex", "config.toml")
2759
+ },
2760
+ {
2761
+ label: ".snipara/templates/codex-config.toml",
2762
+ filePath: path.join(process.cwd(), ".snipara", "templates", "codex-config.toml")
2763
+ },
2764
+ {
2765
+ label: "~/.codex/config.toml",
2766
+ filePath: path.join(homedir(), ".codex", "config.toml")
2767
+ }
2768
+ ];
2769
+ const present = candidates.filter((candidate) => fs.existsSync(candidate.filePath)).map((candidate) => ({
2770
+ label: candidate.label,
2771
+ content: fs.readFileSync(candidate.filePath, "utf-8")
2772
+ }));
2773
+ if (present.length === 0) {
2774
+ return {
2775
+ name: "codex_config",
2776
+ status: "warn",
2777
+ detail: "No Codex config found; merge .snipara/templates/codex-config.toml into ~/.codex/config.toml and restart Codex"
2778
+ };
2779
+ }
2780
+ const configuredBearerEnvVars = present.flatMap(
2781
+ (candidate) => Array.from(candidate.content.matchAll(/bearer_token_env_var\s*=\s*["']([^"']+)["']/g)).map(
2782
+ (match) => ({
2783
+ label: candidate.label,
2784
+ value: match[1]
2785
+ })
2786
+ )
2787
+ );
2788
+ const usesSniparaEnv = configuredBearerEnvVars.some(
2789
+ (candidate) => candidate.value === "SNIPARA_API_KEY"
2790
+ );
2791
+ const nonSniparaBearerEnvVars = configuredBearerEnvVars.filter(
2792
+ (candidate) => candidate.value !== "SNIPARA_API_KEY"
2793
+ );
2794
+ const hasHostedEndpoint = present.some(
2795
+ (candidate) => candidate.content.includes(`${API_URL}/mcp/`)
2796
+ );
2797
+ const labels = present.map((candidate) => candidate.label).join(", ");
2798
+ if (nonSniparaBearerEnvVars.length > 0) {
2799
+ const details = nonSniparaBearerEnvVars.map((candidate) => `${candidate.label} uses ${candidate.value}`).join("; ");
2800
+ return {
2801
+ name: "codex_config",
2802
+ status: "warn",
2803
+ detail: `Codex config uses a non-Snipara bearer env var (${details}); use SNIPARA_API_KEY and restart Codex`
2804
+ };
2805
+ }
2806
+ if (!usesSniparaEnv || !hasHostedEndpoint) {
2807
+ return {
2808
+ name: "codex_config",
2809
+ status: "warn",
2810
+ detail: `Codex config found in ${labels}, but expected hosted MCP endpoint plus bearer_token_env_var = "SNIPARA_API_KEY"`
2811
+ };
2812
+ }
2813
+ return {
2814
+ name: "codex_config",
2815
+ status: "pass",
2816
+ detail: `Codex config uses hosted MCP and SNIPARA_API_KEY (${labels}); restart Codex after config changes`
2817
+ };
2818
+ }
2819
+ function getMistralConfigCheck() {
2820
+ const candidates = [
2821
+ {
2822
+ label: ".vibe/config.toml",
2823
+ filePath: path.join(process.cwd(), ".vibe", "config.toml")
2824
+ },
2825
+ {
2826
+ label: ".snipara/templates/mistral-vibe-config.toml",
2827
+ filePath: path.join(process.cwd(), ".snipara", "templates", "mistral-vibe-config.toml")
2828
+ },
2829
+ {
2830
+ label: "~/.vibe/config.toml",
2831
+ filePath: path.join(homedir(), ".vibe", "config.toml")
2832
+ }
2833
+ ];
2834
+ const present = candidates.filter((candidate) => fs.existsSync(candidate.filePath)).map((candidate) => ({
2835
+ label: candidate.label,
2836
+ content: fs.readFileSync(candidate.filePath, "utf-8")
2837
+ }));
2838
+ if (present.length === 0) {
2839
+ return {
2840
+ name: "mistral_config",
2841
+ status: "warn",
2842
+ detail: "No Mistral Vibe config found; use .snipara/templates/mistral-vibe-config.toml or add Snipara as a Le Chat Custom MCP Connector"
2843
+ };
2844
+ }
2845
+ const hasHostedEndpoint = present.some(
2846
+ (candidate) => candidate.content.includes(`${API_URL}/mcp/`)
2847
+ );
2848
+ const hasSniparaEnv = present.some(
2849
+ (candidate) => candidate.content.includes('api_key_env = "SNIPARA_API_KEY"')
2850
+ );
2851
+ const labels = present.map((candidate) => candidate.label).join(", ");
2852
+ if (!hasHostedEndpoint || !hasSniparaEnv) {
2853
+ return {
2854
+ name: "mistral_config",
2855
+ status: "warn",
2856
+ detail: `Mistral config found in ${labels}, but expected hosted MCP endpoint plus api_key_env = "SNIPARA_API_KEY"`
2857
+ };
2858
+ }
2859
+ return {
2860
+ name: "mistral_config",
2861
+ status: "pass",
2862
+ detail: `Mistral Vibe config uses hosted MCP and SNIPARA_API_KEY (${labels}); restart Vibe after config changes`
2863
+ };
2864
+ }
2552
2865
  function printDoctorReport(detection, report) {
2553
2866
  const targetClient = report.checks.find((check) => check.name === "target_client")?.detail || detection.aiClient;
2554
2867
  console.log(chalk.bold("\n\u{1FA7A} Snipara Doctor\n"));
@@ -2594,16 +2907,22 @@ async function testHostedConnection(answers) {
2594
2907
  spinner.succeed("Connected to Snipara Hosted MCP.");
2595
2908
  const toolNames = extractMcpToolNames(toolsResponse.data);
2596
2909
  const hasSniparaTools = toolNames.some((name) => name.startsWith("snipara_"));
2910
+ const advertisesLegacyTools = toolNames.some((name) => name.startsWith("rlm_"));
2597
2911
  const checks = [
2598
2912
  {
2599
2913
  name: "hosted_connection",
2600
2914
  status: "pass",
2601
- detail: "tools/list succeeded"
2915
+ detail: "real tools/list succeeded"
2602
2916
  },
2603
2917
  {
2604
- name: "hosted_tool_aliases",
2918
+ name: "hosted_snipara_tools",
2605
2919
  status: hasSniparaTools ? "pass" : "warn",
2606
- detail: hasSniparaTools ? "snipara_* tools are exposed by Hosted MCP" : "tools/list succeeded, but expected snipara_* tools were not detected"
2920
+ detail: hasSniparaTools ? "snipara_* tools are advertised by Hosted MCP" : "tools/list succeeded, but expected snipara_* tools were not detected"
2921
+ },
2922
+ {
2923
+ name: "hosted_legacy_tools_hidden",
2924
+ status: advertisesLegacyTools ? "warn" : "pass",
2925
+ detail: advertisesLegacyTools ? "tools/list still advertises legacy rlm_* tools; update or redeploy the hosted MCP surface" : "tools/list does not advertise legacy rlm_* duplicates"
2607
2926
  }
2608
2927
  ];
2609
2928
  const settingsTool = "snipara_settings";
@@ -2713,13 +3032,25 @@ function formatCheckStatus(check) {
2713
3032
  }
2714
3033
  function printSetupVerdict(answers, report) {
2715
3034
  const hostedConnection = getCheck(report, "hosted_connection");
2716
- const toolAliases = getCheck(report, "hosted_tool_aliases");
3035
+ const sniparaTools = getCheck(report, "hosted_snipara_tools");
3036
+ const legacyTools = getCheck(report, "hosted_legacy_tools_hidden");
3037
+ const codexConfig = getCheck(report, "codex_config");
3038
+ const mistralConfig = getCheck(report, "mistral_config");
2717
3039
  const claudeTools = getCheck(report, "claude_tools_visible");
2718
3040
  console.log(chalk.bold("\nSetup verdict:\n"));
2719
3041
  console.log(` Config written: ${answers.profile !== "runtime-only" ? "OK" : "not requested"}`);
2720
3042
  console.log(` Hosted MCP: ${formatCheckStatus(hostedConnection)}`);
2721
- if (toolAliases) {
2722
- console.log(` Tool aliases: ${formatCheckStatus(toolAliases)}`);
3043
+ if (sniparaTools) {
3044
+ console.log(` Snipara tools: ${formatCheckStatus(sniparaTools)}`);
3045
+ }
3046
+ if (legacyTools) {
3047
+ console.log(` Legacy tool duplicates: ${formatCheckStatus(legacyTools)}`);
3048
+ }
3049
+ if (codexConfig) {
3050
+ console.log(` Codex config: ${formatCheckStatus(codexConfig)}`);
3051
+ }
3052
+ if (mistralConfig) {
3053
+ console.log(` Mistral config: ${formatCheckStatus(mistralConfig)}`);
2723
3054
  }
2724
3055
  if (answers.aiClient === "claude-code" || answers.aiClient === "claude-desktop") {
2725
3056
  console.log(
@@ -2804,6 +3135,23 @@ function printSuccess(answers, report) {
2804
3135
  printNextStep(
2805
3136
  "For Codex, merge .snipara/templates/codex-config.toml into ~/.codex/config.toml if the app does not read project .codex/config.toml."
2806
3137
  );
3138
+ printNextStep(
3139
+ "Restart Codex after changing ~/.codex/config.toml so it rediscovers the hosted Snipara MCP tools."
3140
+ );
3141
+ printNextStep(
3142
+ "If Codex shows only a minimal Snipara tool set, force discovery for snipara_recall, snipara_context_query, and snipara_settings."
3143
+ );
3144
+ }
3145
+ if (answers.aiClient === "mistral") {
3146
+ printNextStep(
3147
+ "For Mistral Le Chat, add .snipara/templates/mistral-le-chat-mcp.json as a Custom MCP Connector reference in Connectors."
3148
+ );
3149
+ printNextStep(
3150
+ "For Mistral Vibe, keep .vibe/config.toml or ~/.vibe/config.toml on the generated SNIPARA_API_KEY-backed streamable-http server block, then restart Vibe."
3151
+ );
3152
+ printNextStep(
3153
+ "For LangChain JavaScript, adapt .snipara/templates/mistral-langchain-tools.ts to bind Snipara recall, context_query, and settings tools to ChatMistralAI."
3154
+ );
2807
3155
  }
2808
3156
  printNextStep("Open .snipara/companion/README.md for local commands and repair flow.");
2809
3157
  printNextStep("Run create-snipara doctor anytime you want a fresh local validation report.");
@@ -2812,7 +3160,7 @@ function printSuccess(answers, report) {
2812
3160
  `${COMPANION_COMMAND_NAME} ${MIN_COMPANION_VERSION}+ is installed for low-latency local hooks.`
2813
3161
  );
2814
3162
  printNextStep(
2815
- `Use ${COMPANION_NPX_COMMAND} query / plan / upload / task-commit for explicit local workflows.`
3163
+ `Use ${COMPANION_NPX_COMMAND} recall / query / plan / upload / task-commit for explicit local workflows and as a fallback when an AI client exposes only a partial MCP surface.`
2816
3164
  );
2817
3165
  printNextStep(
2818
3166
  `For business folders, preview with: ${COMPANION_NPX_COMMAND} onboard-folder ./client-export --source-provider local_folder --write-manifest ./snipara-onboard.json`
@@ -2899,6 +3247,7 @@ function detectEnvironment() {
2899
3247
  claudeProjectSettings: fs.existsSync(path.join(process.cwd(), ".claude", "settings.json")),
2900
3248
  claudeDesktopConfig: fs.existsSync(claudePaths.desktopConfigPath),
2901
3249
  cursorDir: fs.existsSync(path.join(homedir(), ".cursor")),
3250
+ mistralVibeConfig: fs.existsSync(path.join(process.cwd(), ".vibe", "config.toml")) || fs.existsSync(path.join(homedir(), ".vibe", "config.toml")),
2902
3251
  sniparaCompanionPack: fs.existsSync(path.join(process.cwd(), ".snipara", "companion")),
2903
3252
  sniparaTemplates: fs.existsSync(path.join(process.cwd(), ".snipara", "templates"))
2904
3253
  },
@@ -3003,6 +3352,16 @@ function collectConfiguredProjectSlugSignals(existingConfig = getExistingConfig(
3003
3352
  path.join(process.cwd(), ".snipara", "templates", "codex-config.toml")
3004
3353
  )
3005
3354
  },
3355
+ {
3356
+ source: ".vibe/config.toml",
3357
+ slug: readProjectSlugFromTextFile(path.join(process.cwd(), ".vibe", "config.toml"))
3358
+ },
3359
+ {
3360
+ source: ".snipara/templates/mistral-vibe-config.toml",
3361
+ slug: readProjectSlugFromTextFile(
3362
+ path.join(process.cwd(), ".snipara", "templates", "mistral-vibe-config.toml")
3363
+ )
3364
+ },
3006
3365
  { source: ".env.local", slug: readProjectSlugFromEnvFile(".env.local") },
3007
3366
  { source: ".env", slug: readProjectSlugFromEnvFile(".env") }
3008
3367
  ];
@@ -3208,6 +3567,9 @@ function detectAiClient() {
3208
3567
  if (fs.existsSync(path.join(process.cwd(), ".gemini"))) {
3209
3568
  return "gemini";
3210
3569
  }
3570
+ if (fs.existsSync(path.join(process.cwd(), ".vibe"))) {
3571
+ return "mistral";
3572
+ }
3211
3573
  if (fs.existsSync(path.join(process.cwd(), ".continue"))) {
3212
3574
  return "continue";
3213
3575
  }
@@ -3226,6 +3588,9 @@ function detectAiClient() {
3226
3588
  if (fs.existsSync(path.join(homedir(), ".gemini")) || process.env.GEMINI_CLI) {
3227
3589
  return "gemini";
3228
3590
  }
3591
+ if (fs.existsSync(path.join(homedir(), ".vibe")) || process.env.MISTRAL_VIBE) {
3592
+ return "mistral";
3593
+ }
3229
3594
  if (fs.existsSync(path.join(homedir(), ".continue")) || process.env.CONTINUE_GLOBAL_DIR) {
3230
3595
  return "continue";
3231
3596
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-snipara",
3
- "version": "1.3.40",
3
+ "version": "1.3.41",
4
4
  "description": "Onboard Snipara Hosted MCP Context + Memory with auth, companion workflows, and optional Snipara Sandbox",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -34,6 +34,7 @@
34
34
  "cursor",
35
35
  "windsurf",
36
36
  "gemini",
37
+ "mistral",
37
38
  "vscode",
38
39
  "continue",
39
40
  "claude-desktop"