create-snipara 1.3.40 → 1.3.42
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 +18 -4
- package/dist/index.js +377 -12
- 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.
|
|
16
|
+
version: "1.3.42",
|
|
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(
|
|
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: "
|
|
2918
|
+
name: "hosted_snipara_tools",
|
|
2605
2919
|
status: hasSniparaTools ? "pass" : "warn",
|
|
2606
|
-
detail: hasSniparaTools ? "snipara_* tools are
|
|
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
|
|
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 (
|
|
2722
|
-
console.log(`
|
|
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.
|
|
3
|
+
"version": "1.3.42",
|
|
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"
|