create-snipara 1.3.35 → 1.3.37

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 +6 -1
  2. package/dist/index.js +130 -46
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -106,7 +106,8 @@ That distinction matters:
106
106
  - installs optional `snipara-orchestrator` for proof gates, drift checks, and htask workflows when requested
107
107
  - configures `.mcp.json`
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
- - creates `AGENTS.md`, `CLAUDE.md`, `.cursor/rules/snipara.mdc`, and `.codex/config.toml` when those files do not already exist
109
+ - creates `AGENTS.md` and `CLAUDE.md` when missing, or appends a marked Snipara section when those files already exist
110
+ - creates `.cursor/rules/snipara.mdc` and `.codex/config.toml` when those files do not already exist
110
111
  - writes client-specific companion packs such as `.snipara/companion/CODEX.md` or `.snipara/companion/GEMINI.md`
111
112
  - updates `.env`, `.env.local`, and `.env.example`
112
113
  - generates `.snipara/companion/README.md`
@@ -162,6 +163,10 @@ The generated agent templates cover:
162
163
  - ChatGPT/OpenAI HTTP MCP snippet
163
164
  - Gemini, VS Code, Continue, and custom HTTP MCP reference snippets
164
165
 
166
+ Existing root `AGENTS.md` and `CLAUDE.md` files are not replaced. The installer
167
+ adds or refreshes a marked Snipara section so project-specific instructions stay
168
+ intact.
169
+
165
170
  When `snipara-companion` is installed, the companion pack also includes the compaction-safe managed workflow:
166
171
 
167
172
  ```bash
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.35",
16
+ version: "1.3.37",
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",
@@ -163,10 +163,19 @@ function getCompanionInitClientFlag(aiClient) {
163
163
  const companionClient = supportedClientMap[aiClient];
164
164
  return companionClient ? ` --client ${companionClient}` : "";
165
165
  }
166
+ function getAutomationClientId(aiClient) {
167
+ if (aiClient === "other" || aiClient === "claude-desktop") {
168
+ return "custom";
169
+ }
170
+ return aiClient;
171
+ }
166
172
  function buildProgram() {
167
173
  const program = new Command();
168
174
  program.name("create-snipara").description("Scaffold hosted Snipara MCP config plus companion workflow helpers").version(VERSION);
169
- 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(
175
+ 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(
176
+ "--client <client>",
177
+ "AI client: claude-code, cursor, windsurf, codex, gemini, chatgpt, vscode, continue, claude-desktop, other"
178
+ ).option("--advanced", "Show advanced profile, project id, hooks, GitHub, and sandbox prompts").option(
170
179
  "--profile <profile>",
171
180
  "Install profile: hosted-only, hosted-companion, full-stack, runtime-only"
172
181
  ).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").addOption(new Option("--full", "Legacy alias for --profile full-stack").hideHelp()).option("-y, --yes", "Accept defaults without prompting").action(async (options) => {
@@ -200,7 +209,10 @@ function buildProgram() {
200
209
  return program;
201
210
  }
202
211
  function addSharedRootOptions(program) {
203
- 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(
212
+ program.option("--api-key <key>", "Snipara API key").option("--slug <slug>", "Project slug").option("--project-id <id>", "Project ID").option(
213
+ "--client <client>",
214
+ "AI client: claude-code, cursor, windsurf, codex, gemini, chatgpt, vscode, continue, claude-desktop, other"
215
+ ).option("--advanced", "Show advanced profile, project id, hooks, GitHub, and sandbox prompts").option(
204
216
  "--profile <profile>",
205
217
  "Install profile: hosted-only, hosted-companion, full-stack, runtime-only"
206
218
  ).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").addOption(new Option("--full", "Legacy alias for --profile full-stack").hideHelp()).option("-y, --yes", "Accept defaults without prompting");
@@ -233,6 +245,7 @@ function parseInitOptionsFromArgv(args) {
233
245
  "--api-key": "apiKey",
234
246
  "--slug": "slug",
235
247
  "--project-id": "projectId",
248
+ "--client": "client",
236
249
  "--profile": "profile",
237
250
  "--github-path": "githubPath"
238
251
  };
@@ -285,6 +298,9 @@ async function runInit(options) {
285
298
  if (answers.profile !== "runtime-only" || answers.installRuntime) {
286
299
  await createMcpConfig(answers);
287
300
  }
301
+ if (answers.aiClient === "claude-code" && answers.profile !== "runtime-only") {
302
+ await createClaudeCodeConfig(answers);
303
+ }
288
304
  await createProjectBinding(answers);
289
305
  await createAgentTemplates(answers);
290
306
  if (answers.installHooks && answers.aiClient === "claude-code") {
@@ -292,6 +308,7 @@ async function runInit(options) {
292
308
  }
293
309
  await updateEnvFiles(answers);
294
310
  await createCompanionConfig(answers);
311
+ await syncRemoteAutomationClient(answers, { enabled: !options.skipTest });
295
312
  await createCompanionPack(answers);
296
313
  const validationReport = await runValidation(answers, !options.skipTest);
297
314
  writeDoctorReport(validationReport);
@@ -331,6 +348,9 @@ async function runRepair(options) {
331
348
  await installNodeCompanion(true);
332
349
  }
333
350
  await createMcpConfig(answers);
351
+ if (answers.aiClient === "claude-code" && answers.profile !== "runtime-only") {
352
+ await createClaudeCodeConfig(answers);
353
+ }
334
354
  await createProjectBinding(answers);
335
355
  await createAgentTemplates(answers);
336
356
  await updateEnvFiles(answers);
@@ -362,6 +382,9 @@ async function runUpgrade(options) {
362
382
  if (options.withMcp || options.withRuntime || detection.files.mcpConfig) {
363
383
  await createMcpConfig(upgradedAnswers);
364
384
  }
385
+ if (upgradedAnswers.aiClient === "claude-code" && upgradedAnswers.profile !== "runtime-only") {
386
+ await createClaudeCodeConfig(upgradedAnswers);
387
+ }
365
388
  await createProjectBinding(upgradedAnswers);
366
389
  await createAgentTemplates(upgradedAnswers);
367
390
  await createCompanionConfig(upgradedAnswers);
@@ -445,6 +468,7 @@ function printBanner() {
445
468
  async function gatherConfiguration(options) {
446
469
  console.log(chalk.bold("\n\u{1F4CB} Project Configuration\n"));
447
470
  const detectedClient = detectAiClient();
471
+ const requestedClient = normalizeClientOption(options.client);
448
472
  const existingConfig = getExistingConfig();
449
473
  const projectSlugResolution = resolveProjectSlug(existingConfig);
450
474
  let detectedSlug = projectSlugResolution.slug;
@@ -477,7 +501,7 @@ async function gatherConfiguration(options) {
477
501
  apiKey: providedApiKey || "YOUR_API_KEY_HERE",
478
502
  apiKeyType: providedApiKey ? "api" : "none",
479
503
  profile: initialProfile,
480
- aiClient: detectedClient,
504
+ aiClient: requestedClient || detectedClient,
481
505
  skipCompanion: !!options.skipCompanion,
482
506
  skipMcp: !!options.skipMcp,
483
507
  skipRuntime: !!options.skipRuntime,
@@ -544,7 +568,9 @@ async function gatherConfiguration(options) {
544
568
  message: "Which AI client are you using?",
545
569
  initial: Math.max(
546
570
  0,
547
- AI_CLIENT_CHOICES.findIndex((choice) => choice.value === detectedClient)
571
+ AI_CLIENT_CHOICES.findIndex(
572
+ (choice) => choice.value === (requestedClient || detectedClient)
573
+ )
548
574
  ),
549
575
  choices: AI_CLIENT_CHOICES
550
576
  },
@@ -595,7 +621,7 @@ async function gatherConfiguration(options) {
595
621
  apiKey: apiKey || "",
596
622
  apiKeyType,
597
623
  profile: response.profile || initialProfile,
598
- aiClient: response.aiClient || detectedClient,
624
+ aiClient: requestedClient || response.aiClient || detectedClient,
599
625
  skipCompanion: !!options.skipCompanion,
600
626
  skipMcp: !!options.skipMcp,
601
627
  skipRuntime: !!options.skipRuntime,
@@ -693,7 +719,8 @@ function normalizeClientOption(value) {
693
719
  chatgpt: "chatgpt",
694
720
  vscode: "vscode",
695
721
  continue: "continue",
696
- other: "other"
722
+ other: "other",
723
+ custom: "other"
697
724
  };
698
725
  const client = aliases[normalized];
699
726
  if (!client) {
@@ -861,10 +888,14 @@ async function runDeviceAuthorization() {
861
888
  spinner.fail("Authorization expired");
862
889
  throw new Error("device code expired");
863
890
  }
864
- async function postJson(url, body, allowErrorResponse = false) {
891
+ async function postJson(url, body, allowErrorResponse = false, init = {}) {
892
+ const headers = {
893
+ ...init.headers ?? {},
894
+ "Content-Type": "application/json"
895
+ };
865
896
  const response = await fetch(url, {
866
- method: "POST",
867
- headers: { "Content-Type": "application/json" },
897
+ method: init.method ?? "POST",
898
+ headers,
868
899
  body: JSON.stringify(body)
869
900
  });
870
901
  const data = await response.json().catch(() => ({}));
@@ -1070,13 +1101,7 @@ async function createMcpConfig(answers) {
1070
1101
  }
1071
1102
  }
1072
1103
  if (answers.profile !== "runtime-only") {
1073
- mcpConfig.mcpServers.snipara = {
1074
- type: "http",
1075
- url: `${API_URL}/mcp/${answers.projectSlug}`,
1076
- headers: {
1077
- "X-API-Key": answers.apiKey || "YOUR_API_KEY_HERE"
1078
- }
1079
- };
1104
+ mcpConfig.mcpServers.snipara = buildHostedMcpServer(answers);
1080
1105
  }
1081
1106
  if (answers.installRuntime) {
1082
1107
  mcpConfig.mcpServers[SANDBOX_SERVER_NAME] = {
@@ -1096,6 +1121,48 @@ async function createMcpConfig(answers) {
1096
1121
  await createClaudeDesktopConfig(answers);
1097
1122
  }
1098
1123
  }
1124
+ function buildHostedMcpServer(answers) {
1125
+ return {
1126
+ type: "http",
1127
+ url: buildHostedMcpEndpoint(answers),
1128
+ headers: {
1129
+ "X-API-Key": answers.apiKey || "YOUR_API_KEY_HERE"
1130
+ }
1131
+ };
1132
+ }
1133
+ function asObjectRecord(value) {
1134
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1135
+ }
1136
+ async function createClaudeCodeConfig(answers) {
1137
+ if (answers.profile === "runtime-only") {
1138
+ return;
1139
+ }
1140
+ const claudeDir = path.join(homedir(), ".claude");
1141
+ const settingsPath = path.join(claudeDir, "settings.json");
1142
+ console.log(chalk.bold("\n\u{1F916} Configuring Claude Code MCP\n"));
1143
+ try {
1144
+ fs.mkdirSync(claudeDir, { recursive: true });
1145
+ let settings = {};
1146
+ if (fs.existsSync(settingsPath)) {
1147
+ settings = asObjectRecord(JSON.parse(fs.readFileSync(settingsPath, "utf-8")));
1148
+ }
1149
+ const mcpServers = asObjectRecord(settings.mcpServers);
1150
+ settings.mcpServers = {
1151
+ ...mcpServers,
1152
+ snipara: buildHostedMcpServer(answers)
1153
+ };
1154
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", { mode: 384 });
1155
+ try {
1156
+ fs.chmodSync(settingsPath, 384);
1157
+ } catch {
1158
+ }
1159
+ console.log(chalk.green(" \u2713 Updated ~/.claude/settings.json with Snipara MCP"));
1160
+ } catch (error) {
1161
+ const detail = error instanceof Error ? error.message : String(error);
1162
+ console.log(chalk.yellow(` \u26A0\uFE0F Could not update Claude Code MCP config: ${detail}`));
1163
+ console.log(chalk.dim(" Run create-snipara doctor --client claude after manual setup."));
1164
+ }
1165
+ }
1099
1166
  async function createProjectBinding(answers) {
1100
1167
  if (answers.profile === "runtime-only") {
1101
1168
  return;
@@ -1383,13 +1450,7 @@ async function createClaudeDesktopConfig(answers) {
1383
1450
  config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
1384
1451
  }
1385
1452
  config.mcpServers = config.mcpServers || {};
1386
- config.mcpServers.snipara = {
1387
- type: "http",
1388
- url: buildHostedMcpEndpoint(answers),
1389
- headers: {
1390
- "X-API-Key": answers.apiKey
1391
- }
1392
- };
1453
+ config.mcpServers.snipara = buildHostedMcpServer(answers);
1393
1454
  if (answers.installRuntime) {
1394
1455
  config.mcpServers[SANDBOX_SERVER_NAME] = {
1395
1456
  command: detectSandboxCommand() || SANDBOX_COMMAND_NAME,
@@ -1640,31 +1701,49 @@ async function createCompanionConfig(answers) {
1640
1701
  }
1641
1702
  const configDir = path.join(process.cwd(), ".snipara", "companion");
1642
1703
  const configPath = path.join(configDir, "config.json");
1643
- const globalConfigPath = getGlobalCompanionConfigPath();
1644
1704
  fs.mkdirSync(configDir, { recursive: true });
1645
- fs.mkdirSync(path.dirname(globalConfigPath), { recursive: true });
1646
- const existingGlobalConfig = readCompanionConfigFile(globalConfigPath);
1647
1705
  const existingWorkspaceConfig = readCompanionConfigFile(configPath);
1648
- const globalConfig = {
1649
- ...existingGlobalConfig,
1650
- apiKey: answers.apiKey,
1651
- apiUrl: API_URL
1652
- };
1653
1706
  const workspaceConfig = {
1654
- projectId: answers.projectSlug,
1655
- sessionId: typeof existingWorkspaceConfig.sessionId === "string" && existingWorkspaceConfig.sessionId ? existingWorkspaceConfig.sessionId : `sess_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
1707
+ ...existingWorkspaceConfig,
1708
+ apiKey: answers.apiKey,
1709
+ apiUrl: API_URL,
1710
+ projectId: answers.projectId || answers.projectSlug,
1711
+ sessionId: typeof existingWorkspaceConfig.sessionId === "string" && existingWorkspaceConfig.sessionId ? existingWorkspaceConfig.sessionId : `sess_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
1712
+ client: getAutomationClientId(answers.aiClient)
1656
1713
  };
1657
- fs.writeFileSync(globalConfigPath, JSON.stringify(globalConfig, null, 2) + "\n");
1658
1714
  fs.writeFileSync(configPath, JSON.stringify(workspaceConfig, null, 2) + "\n");
1659
- ensureGitignoreEntry(
1660
- ".snipara/companion/config.json",
1661
- "# Snipara local companion workspace config"
1662
- );
1663
- console.log(chalk.green(" \u2713 Configured global companion auth"));
1664
- console.log(chalk.green(" \u2713 Configured local companion workspace"));
1715
+ ensureGitignoreEntry(".snipara/companion/config.json", "# Snipara local companion config");
1716
+ console.log(chalk.green(" \u2713 Configured local companion config"));
1665
1717
  }
1666
- function getGlobalCompanionConfigPath() {
1667
- return path.join(homedir(), ".rlmsaas", "config.json");
1718
+ async function syncRemoteAutomationClient(answers, options) {
1719
+ if (!options.enabled || answers.apiKeyType === "none" || !answers.apiKey || answers.apiKey.includes("YOUR_") || !answers.projectSlug) {
1720
+ return;
1721
+ }
1722
+ const automationClient = getAutomationClientId(answers.aiClient);
1723
+ const url = `${BASE_URL}/api/cli/projects/${encodeURIComponent(
1724
+ answers.projectSlug
1725
+ )}/automation-client`;
1726
+ try {
1727
+ const response = await postJson(
1728
+ url,
1729
+ { automationClient },
1730
+ false,
1731
+ {
1732
+ method: "PATCH",
1733
+ headers: { "X-API-Key": answers.apiKey }
1734
+ }
1735
+ );
1736
+ if (response.success === false) {
1737
+ throw new Error(response.message || "dashboard rejected automation client update");
1738
+ }
1739
+ console.log(chalk.green(` \u2713 Synced dashboard automation client: ${automationClient}`));
1740
+ } catch (error) {
1741
+ console.log(
1742
+ chalk.yellow(
1743
+ ` \u26A0\uFE0F Could not sync dashboard automation client: ${error instanceof Error ? error.message : "unknown error"}`
1744
+ )
1745
+ );
1746
+ }
1668
1747
  }
1669
1748
  function readCompanionConfigFile(filePath) {
1670
1749
  if (!fs.existsSync(filePath)) {
@@ -1899,7 +1978,7 @@ function getClientPackFilename(client) {
1899
1978
  }
1900
1979
  function getClientConfigArtifacts(client) {
1901
1980
  if (client === "claude-code") {
1902
- return ["AGENTS.md", "CLAUDE.md", ".claude/settings.json when hooks are installed"];
1981
+ return ["AGENTS.md", "CLAUDE.md", "~/.claude/settings.json MCP server"];
1903
1982
  }
1904
1983
  if (client === "cursor") {
1905
1984
  return [".mcp.json", ".cursor/rules/snipara.mdc", "dashboard automation bundle when installed"];
@@ -2172,7 +2251,7 @@ function getClaudeClientChecks(answers, detection) {
2172
2251
  checks.push({
2173
2252
  name: "claude_code_settings",
2174
2253
  status: detection.files.claudeSettings ? codeSettingsHasSnipara ? "pass" : "warn" : "warn",
2175
- 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"
2254
+ detail: detection.files.claudeSettings ? codeSettingsHasSnipara ? "~/.claude/settings.json contains a Snipara MCP server" : "~/.claude/settings.json exists but no Snipara MCP server was found" : "~/.claude/settings.json not found; Claude may require Settings -> MCP UI setup"
2176
2255
  });
2177
2256
  }
2178
2257
  if (answers.aiClient === "claude-desktop") {
@@ -2598,6 +2677,11 @@ function printSuccess(answers, report) {
2598
2677
  if (answers.profile !== "runtime-only") {
2599
2678
  console.log(chalk.green(" \u2713 .mcp.json") + chalk.dim(" - Hosted MCP config"));
2600
2679
  }
2680
+ if (answers.aiClient === "claude-code" && answers.profile !== "runtime-only") {
2681
+ console.log(
2682
+ chalk.green(" \u2713 ~/.claude/settings.json") + chalk.dim(" - Claude Code Hosted MCP server")
2683
+ );
2684
+ }
2601
2685
  console.log(chalk.green(" \u2713 .snipara/companion") + chalk.dim(" - Local pack + doctor report"));
2602
2686
  const failedChecks = report.checks.filter((check) => check.status === "fail");
2603
2687
  const warnedChecks = report.checks.filter((check) => check.status === "warn");
@@ -2610,7 +2694,7 @@ function printSuccess(answers, report) {
2610
2694
  printNextStep("Restart your AI client if you created or changed hosted MCP config.");
2611
2695
  if (answers.aiClient === "claude-code") {
2612
2696
  printNextStep(
2613
- "For Claude Code, confirm Snipara appears in Settings -> MCP or ToolSearch; local files alone do not prove Claude loaded the server."
2697
+ "For Claude Code, start a new session and confirm Snipara appears in Settings -> MCP or ToolSearch."
2614
2698
  );
2615
2699
  printNextStep("If Snipara tools are missing, run: npx create-snipara doctor --client claude");
2616
2700
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-snipara",
3
- "version": "1.3.35",
3
+ "version": "1.3.37",
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",