create-snipara 1.3.34 → 1.3.36

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 +1 -6
  2. package/dist/index.js +84 -67
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -106,8 +106,7 @@ 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` 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
109
+ - creates `AGENTS.md`, `CLAUDE.md`, `.cursor/rules/snipara.mdc`, and `.codex/config.toml` when those files do not already exist
111
110
  - writes client-specific companion packs such as `.snipara/companion/CODEX.md` or `.snipara/companion/GEMINI.md`
112
111
  - updates `.env`, `.env.local`, and `.env.example`
113
112
  - generates `.snipara/companion/README.md`
@@ -163,10 +162,6 @@ The generated agent templates cover:
163
162
  - ChatGPT/OpenAI HTTP MCP snippet
164
163
  - Gemini, VS Code, Continue, and custom HTTP MCP reference snippets
165
164
 
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
-
170
165
  When `snipara-companion` is installed, the companion pack also includes the compaction-safe managed workflow:
171
166
 
172
167
  ```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.34",
16
+ version: "1.3.36",
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",
@@ -285,6 +285,9 @@ async function runInit(options) {
285
285
  if (answers.profile !== "runtime-only" || answers.installRuntime) {
286
286
  await createMcpConfig(answers);
287
287
  }
288
+ if (answers.aiClient === "claude-code" && answers.profile !== "runtime-only") {
289
+ await createClaudeCodeConfig(answers);
290
+ }
288
291
  await createProjectBinding(answers);
289
292
  await createAgentTemplates(answers);
290
293
  if (answers.installHooks && answers.aiClient === "claude-code") {
@@ -331,6 +334,9 @@ async function runRepair(options) {
331
334
  await installNodeCompanion(true);
332
335
  }
333
336
  await createMcpConfig(answers);
337
+ if (answers.aiClient === "claude-code" && answers.profile !== "runtime-only") {
338
+ await createClaudeCodeConfig(answers);
339
+ }
334
340
  await createProjectBinding(answers);
335
341
  await createAgentTemplates(answers);
336
342
  await updateEnvFiles(answers);
@@ -362,6 +368,9 @@ async function runUpgrade(options) {
362
368
  if (options.withMcp || options.withRuntime || detection.files.mcpConfig) {
363
369
  await createMcpConfig(upgradedAnswers);
364
370
  }
371
+ if (upgradedAnswers.aiClient === "claude-code" && upgradedAnswers.profile !== "runtime-only") {
372
+ await createClaudeCodeConfig(upgradedAnswers);
373
+ }
365
374
  await createProjectBinding(upgradedAnswers);
366
375
  await createAgentTemplates(upgradedAnswers);
367
376
  await createCompanionConfig(upgradedAnswers);
@@ -1070,13 +1079,7 @@ async function createMcpConfig(answers) {
1070
1079
  }
1071
1080
  }
1072
1081
  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
- };
1082
+ mcpConfig.mcpServers.snipara = buildHostedMcpServer(answers);
1080
1083
  }
1081
1084
  if (answers.installRuntime) {
1082
1085
  mcpConfig.mcpServers[SANDBOX_SERVER_NAME] = {
@@ -1096,6 +1099,48 @@ async function createMcpConfig(answers) {
1096
1099
  await createClaudeDesktopConfig(answers);
1097
1100
  }
1098
1101
  }
1102
+ function buildHostedMcpServer(answers) {
1103
+ return {
1104
+ type: "http",
1105
+ url: buildHostedMcpEndpoint(answers),
1106
+ headers: {
1107
+ "X-API-Key": answers.apiKey || "YOUR_API_KEY_HERE"
1108
+ }
1109
+ };
1110
+ }
1111
+ function asObjectRecord(value) {
1112
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1113
+ }
1114
+ async function createClaudeCodeConfig(answers) {
1115
+ if (answers.profile === "runtime-only") {
1116
+ return;
1117
+ }
1118
+ const claudeDir = path.join(homedir(), ".claude");
1119
+ const settingsPath = path.join(claudeDir, "settings.json");
1120
+ console.log(chalk.bold("\n\u{1F916} Configuring Claude Code MCP\n"));
1121
+ try {
1122
+ fs.mkdirSync(claudeDir, { recursive: true });
1123
+ let settings = {};
1124
+ if (fs.existsSync(settingsPath)) {
1125
+ settings = asObjectRecord(JSON.parse(fs.readFileSync(settingsPath, "utf-8")));
1126
+ }
1127
+ const mcpServers = asObjectRecord(settings.mcpServers);
1128
+ settings.mcpServers = {
1129
+ ...mcpServers,
1130
+ snipara: buildHostedMcpServer(answers)
1131
+ };
1132
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", { mode: 384 });
1133
+ try {
1134
+ fs.chmodSync(settingsPath, 384);
1135
+ } catch {
1136
+ }
1137
+ console.log(chalk.green(" \u2713 Updated ~/.claude/settings.json with Snipara MCP"));
1138
+ } catch (error) {
1139
+ const detail = error instanceof Error ? error.message : String(error);
1140
+ console.log(chalk.yellow(` \u26A0\uFE0F Could not update Claude Code MCP config: ${detail}`));
1141
+ console.log(chalk.dim(" Run create-snipara doctor --client claude after manual setup."));
1142
+ }
1143
+ }
1099
1144
  async function createProjectBinding(answers) {
1100
1145
  if (answers.profile === "runtime-only") {
1101
1146
  return;
@@ -1152,12 +1197,12 @@ async function createAgentTemplates(answers) {
1152
1197
  fs.writeFileSync(template.path, template.content);
1153
1198
  }
1154
1199
  console.log(chalk.green(" Created .snipara/templates agent snippets"));
1155
- writeOrMergeMarkdownInstructions(
1200
+ writeIfMissing(
1156
1201
  path.join(process.cwd(), "AGENTS.md"),
1157
1202
  buildAgentsMdTemplate(answers),
1158
1203
  "AGENTS.md"
1159
1204
  );
1160
- writeOrMergeMarkdownInstructions(
1205
+ writeIfMissing(
1161
1206
  path.join(process.cwd(), "CLAUDE.md"),
1162
1207
  buildClaudeMdTemplate(answers),
1163
1208
  "CLAUDE.md"
@@ -1186,45 +1231,6 @@ function writeIfMissing(filePath, content, label) {
1186
1231
  fs.writeFileSync(filePath, content);
1187
1232
  console.log(chalk.green(` Created ${label}`));
1188
1233
  }
1189
- function markdownInstructionMarker(label, type) {
1190
- return `<!-- snipara:instructions ${label}:${type} -->`;
1191
- }
1192
- function escapeRegExp(value) {
1193
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1194
- }
1195
- function ensureTrailingNewline(content) {
1196
- return content.endsWith("\n") ? content : `${content}
1197
- `;
1198
- }
1199
- function buildMarkdownInstructionBlock(label, content) {
1200
- return [
1201
- markdownInstructionMarker(label, "start"),
1202
- content.trimEnd(),
1203
- markdownInstructionMarker(label, "end"),
1204
- ""
1205
- ].join("\n");
1206
- }
1207
- function writeOrMergeMarkdownInstructions(filePath, content, label) {
1208
- if (!fs.existsSync(filePath)) {
1209
- fs.writeFileSync(filePath, content);
1210
- console.log(chalk.green(` Created ${label}`));
1211
- return;
1212
- }
1213
- const current = fs.readFileSync(filePath, "utf8");
1214
- if (current.trim() === content.trim()) {
1215
- console.log(chalk.dim(` ${label} already contains Snipara instructions`));
1216
- return;
1217
- }
1218
- const block = buildMarkdownInstructionBlock(label, content);
1219
- const start = markdownInstructionMarker(label, "start");
1220
- const end = markdownInstructionMarker(label, "end");
1221
- const existingBlockPattern = new RegExp(
1222
- `${escapeRegExp(start)}[\\s\\S]*?${escapeRegExp(end)}\\n?`
1223
- );
1224
- const nextContent = existingBlockPattern.test(current) ? ensureTrailingNewline(current.replace(existingBlockPattern, block)) : `${current}${current.endsWith("\n\n") ? "" : current.endsWith("\n") ? "\n" : "\n\n"}${block}`;
1225
- fs.writeFileSync(filePath, nextContent);
1226
- console.log(chalk.green(` Updated ${label} with Snipara instructions`));
1227
- }
1228
1234
  function buildHostedMcpEndpoint(answers) {
1229
1235
  return `${API_URL}/mcp/${answers.projectSlug}`;
1230
1236
  }
@@ -1269,8 +1275,7 @@ Agent task lifecycle:
1269
1275
  8. Use \`snipara_plan\` or \`snipara_decompose\` only for FULL-mode work and only
1270
1276
  when the hosted server exposes them.
1271
1277
  9. When the LLM has produced a visible multi-phase plan and \`snipara-companion\`
1272
- is installed, run \`snipara-companion workflow start --goal "<goal>" --plan-file
1273
- <plan_file>\`, then use \`workflow phase-start\`, \`workflow phase-commit\`,
1278
+ is installed, run \`snipara-companion workflow start --goal "<goal>" --plan-file <plan_file>\`, then use \`workflow phase-start\`, \`workflow phase-commit\`,
1274
1279
  \`workflow resume\`, and \`final-commit\` so compaction cannot lose the next
1275
1280
  phase.
1276
1281
  10. Use local file reads, \`rg\`, git commands, and test commands for exact edits,
@@ -1423,13 +1428,7 @@ async function createClaudeDesktopConfig(answers) {
1423
1428
  config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
1424
1429
  }
1425
1430
  config.mcpServers = config.mcpServers || {};
1426
- config.mcpServers.snipara = {
1427
- type: "http",
1428
- url: buildHostedMcpEndpoint(answers),
1429
- headers: {
1430
- "X-API-Key": answers.apiKey
1431
- }
1432
- };
1431
+ config.mcpServers.snipara = buildHostedMcpServer(answers);
1433
1432
  if (answers.installRuntime) {
1434
1433
  config.mcpServers[SANDBOX_SERVER_NAME] = {
1435
1434
  command: detectSandboxCommand() || SANDBOX_COMMAND_NAME,
@@ -1680,18 +1679,31 @@ async function createCompanionConfig(answers) {
1680
1679
  }
1681
1680
  const configDir = path.join(process.cwd(), ".snipara", "companion");
1682
1681
  const configPath = path.join(configDir, "config.json");
1682
+ const globalConfigPath = getGlobalCompanionConfigPath();
1683
1683
  fs.mkdirSync(configDir, { recursive: true });
1684
+ fs.mkdirSync(path.dirname(globalConfigPath), { recursive: true });
1685
+ const existingGlobalConfig = readCompanionConfigFile(globalConfigPath);
1684
1686
  const existingWorkspaceConfig = readCompanionConfigFile(configPath);
1685
- const workspaceConfig = {
1686
- ...existingWorkspaceConfig,
1687
+ const globalConfig = {
1688
+ ...existingGlobalConfig,
1687
1689
  apiKey: answers.apiKey,
1688
- apiUrl: API_URL,
1689
- projectId: answers.projectId || answers.projectSlug,
1690
+ apiUrl: API_URL
1691
+ };
1692
+ const workspaceConfig = {
1693
+ projectId: answers.projectSlug,
1690
1694
  sessionId: typeof existingWorkspaceConfig.sessionId === "string" && existingWorkspaceConfig.sessionId ? existingWorkspaceConfig.sessionId : `sess_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
1691
1695
  };
1696
+ fs.writeFileSync(globalConfigPath, JSON.stringify(globalConfig, null, 2) + "\n");
1692
1697
  fs.writeFileSync(configPath, JSON.stringify(workspaceConfig, null, 2) + "\n");
1693
- ensureGitignoreEntry(".snipara/companion/config.json", "# Snipara local companion config");
1694
- console.log(chalk.green(" \u2713 Configured local companion config"));
1698
+ ensureGitignoreEntry(
1699
+ ".snipara/companion/config.json",
1700
+ "# Snipara local companion workspace config"
1701
+ );
1702
+ console.log(chalk.green(" \u2713 Configured global companion auth"));
1703
+ console.log(chalk.green(" \u2713 Configured local companion workspace"));
1704
+ }
1705
+ function getGlobalCompanionConfigPath() {
1706
+ return path.join(homedir(), ".rlmsaas", "config.json");
1695
1707
  }
1696
1708
  function readCompanionConfigFile(filePath) {
1697
1709
  if (!fs.existsSync(filePath)) {
@@ -1926,7 +1938,7 @@ function getClientPackFilename(client) {
1926
1938
  }
1927
1939
  function getClientConfigArtifacts(client) {
1928
1940
  if (client === "claude-code") {
1929
- return ["AGENTS.md", "CLAUDE.md", ".claude/settings.json when hooks are installed"];
1941
+ return ["AGENTS.md", "CLAUDE.md", "~/.claude/settings.json MCP server"];
1930
1942
  }
1931
1943
  if (client === "cursor") {
1932
1944
  return [".mcp.json", ".cursor/rules/snipara.mdc", "dashboard automation bundle when installed"];
@@ -2199,7 +2211,7 @@ function getClaudeClientChecks(answers, detection) {
2199
2211
  checks.push({
2200
2212
  name: "claude_code_settings",
2201
2213
  status: detection.files.claudeSettings ? codeSettingsHasSnipara ? "pass" : "warn" : "warn",
2202
- 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"
2214
+ 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"
2203
2215
  });
2204
2216
  }
2205
2217
  if (answers.aiClient === "claude-desktop") {
@@ -2625,6 +2637,11 @@ function printSuccess(answers, report) {
2625
2637
  if (answers.profile !== "runtime-only") {
2626
2638
  console.log(chalk.green(" \u2713 .mcp.json") + chalk.dim(" - Hosted MCP config"));
2627
2639
  }
2640
+ if (answers.aiClient === "claude-code" && answers.profile !== "runtime-only") {
2641
+ console.log(
2642
+ chalk.green(" \u2713 ~/.claude/settings.json") + chalk.dim(" - Claude Code Hosted MCP server")
2643
+ );
2644
+ }
2628
2645
  console.log(chalk.green(" \u2713 .snipara/companion") + chalk.dim(" - Local pack + doctor report"));
2629
2646
  const failedChecks = report.checks.filter((check) => check.status === "fail");
2630
2647
  const warnedChecks = report.checks.filter((check) => check.status === "warn");
@@ -2637,7 +2654,7 @@ function printSuccess(answers, report) {
2637
2654
  printNextStep("Restart your AI client if you created or changed hosted MCP config.");
2638
2655
  if (answers.aiClient === "claude-code") {
2639
2656
  printNextStep(
2640
- "For Claude Code, confirm Snipara appears in Settings -> MCP or ToolSearch; local files alone do not prove Claude loaded the server."
2657
+ "For Claude Code, start a new session and confirm Snipara appears in Settings -> MCP or ToolSearch."
2641
2658
  );
2642
2659
  printNextStep("If Snipara tools are missing, run: npx create-snipara doctor --client claude");
2643
2660
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-snipara",
3
- "version": "1.3.34",
3
+ "version": "1.3.36",
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",