create-snipara 1.3.17 → 1.3.19

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 +20 -6
  2. package/dist/index.js +108 -25
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -52,6 +52,7 @@ The default CLI path wires the local companion around those surfaces:
52
52
 
53
53
  - `snipara-companion` for local workflow commands and managed phase commits; legacy `rlm-hook` commands remain compatible
54
54
  - `snipara-sandbox` for optional local code execution
55
+ - `snipara-orchestrator` only when explicitly requested with `--with-orchestrator`
55
56
  - local config, env files, hook scripts, and a project companion pack
56
57
  - `snipara-mcp` only for local stdio compatibility, development, or testing
57
58
 
@@ -59,12 +60,12 @@ The default CLI path wires the local companion around those surfaces:
59
60
 
60
61
  The CLI now works around explicit install profiles:
61
62
 
62
- | Profile | What it installs | Best for |
63
- | ------------------ | --------------------------------------------- | --------------------------------------------------- |
64
- | `hosted-companion` | MCP + `snipara-companion` | Default: hosted context plus managed local workflow |
65
- | `hosted-only` | Hosted MCP config only | Locked-down or minimal environments |
66
- | `full-stack` | MCP + `snipara-companion` + `snipara-sandbox` | Hosted context plus sandboxed local execution |
67
- | `runtime-only` | `snipara-sandbox` only | Local execution without hosted API |
63
+ | Profile | What it installs | Best for |
64
+ | ------------------ | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
65
+ | `hosted-companion` | MCP + `snipara-companion` | Default: hosted context plus managed local workflow |
66
+ | `hosted-only` | Hosted MCP config only | Locked-down or minimal environments |
67
+ | `full-stack` | MCP + `snipara-companion` + `snipara-sandbox` | Hosted context plus sandboxed local execution. Does not include orchestrator unless `--with-orchestrator` is passed |
68
+ | `runtime-only` | `snipara-sandbox` only | Local execution without hosted API |
68
69
 
69
70
  Default profile: `hosted-companion`
70
71
 
@@ -102,6 +103,7 @@ That distinction matters:
102
103
  - explains the GitHub PR Answer Packs path for AI-assisted pull request workflows
103
104
  - installs `snipara-companion` by default for local managed workflows
104
105
  - installs optional `snipara-sandbox`
106
+ - installs optional `snipara-orchestrator` for proof gates, drift checks, and htask workflows when requested
105
107
  - configures `.mcp.json`
106
108
  - generates merge-ready AI agent templates in `.snipara/templates/`, including Pro+ symbol-card, code-impact, and managed workflow guidance for paid Context plans
107
109
  - creates `AGENTS.md`, `CLAUDE.md`, `.cursor/rules/snipara.mdc`, and `.codex/config.toml` when those files do not already exist
@@ -140,6 +142,8 @@ By default, this uses the recommended `hosted-companion` setup: hosted MCP plus
140
142
  the local `snipara-companion` workflow helper. Legacy `rlm-hook` commands remain
141
143
  compatible. Add `--advanced` when you want profile selection, project id entry,
142
144
  hook prompts, GitHub automation prompts, or sandbox provider prompts.
145
+ Use `--with-orchestrator` only when you explicitly need the advanced production
146
+ validation and htask package; it is not part of `full-stack` by default.
143
147
 
144
148
  The advanced flow asks for:
145
149
 
@@ -421,6 +425,16 @@ npx create-snipara --profile full-stack --advanced
421
425
 
422
426
  Best when the user also wants sandboxed `snipara-sandbox` execution.
423
427
 
428
+ ### Full local stack plus production orchestration
429
+
430
+ ```bash
431
+ npx create-snipara --profile full-stack --with-orchestrator
432
+ ```
433
+
434
+ Best when the user wants Hosted MCP, companion workflows, Sandbox execution, and
435
+ the explicit `snipara-orchestrator` package for production gates, drift checks,
436
+ or htask coordination.
437
+
424
438
  ### Connect the current repository
425
439
 
426
440
  ```bash
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import fs from "fs";
9
9
  import path from "path";
10
10
  import { execSync, spawn } from "child_process";
11
11
  import { homedir } from "os";
12
- var VERSION = "1.3.17";
12
+ var VERSION = "1.3.19";
13
13
  var BASE_URL = "https://www.snipara.com";
14
14
  var API_URL = "https://api.snipara.com";
15
15
  var COMPANION_PACKAGE_NAME = "snipara-companion";
@@ -18,10 +18,12 @@ var LEGACY_COMPANION_COMMAND_NAME = "rlm-hook";
18
18
  var GITHUB_ANSWER_PACKS_LABEL = "GitHub PR Answer Packs";
19
19
  var MCP_PACKAGE_NAME = "snipara-mcp";
20
20
  var SANDBOX_PACKAGE_NAME = "snipara-sandbox[all]";
21
+ var ORCHESTRATOR_PACKAGE_NAME = "snipara-orchestrator";
21
22
  var SANDBOX_SERVER_NAME = "snipara-sandbox";
22
23
  var LEGACY_RUNTIME_SERVER_NAME = "rlm-runtime";
23
24
  var SANDBOX_COMMAND_NAME = "snipara-sandbox";
24
25
  var LEGACY_SANDBOX_COMMAND_NAME = "rlm";
26
+ var ORCHESTRATOR_COMMAND_NAME = "snipara-orchestrator";
25
27
  var DEVICE_CLIENT_ID = "create-snipara";
26
28
  var DeviceAuthorizationAborted = class extends Error {
27
29
  };
@@ -94,7 +96,7 @@ function buildProgram() {
94
96
  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(
95
97
  "--profile <profile>",
96
98
  "Install profile: hosted-only, hosted-companion, full-stack, runtime-only"
97
- ).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("--github", "Open GitHub repository automation setup when a git remote is detected").option("--skip-github", "Skip the GitHub automation prompt").option("--github-path <path>", "Optional GitHub path filter for the initial repository sync").option("--runtime-only", "Install only Snipara Sandbox").option("-y, --yes", "Accept defaults without prompting").action(async (options) => {
99
+ ).option("--skip-companion", "Skip local companion CLI installation").option("--skip-mcp", "Skip local snipara-mcp package installation").option("--skip-runtime", "Skip snipara-sandbox installation").option("--skip-hooks", "Skip hook generation").option("--skip-test", "Skip hosted connection test").option("--with-orchestrator", "Install optional snipara-orchestrator package").option("--github", "Open GitHub repository automation setup when a git remote is detected").option("--skip-github", "Skip the GitHub automation prompt").option("--github-path <path>", "Optional GitHub path filter for the initial repository sync").option("--runtime-only", "Install only Snipara Sandbox").option("-y, --yes", "Accept defaults without prompting").action(async (options) => {
98
100
  printBanner();
99
101
  await runInit(options);
100
102
  });
@@ -102,11 +104,11 @@ function buildProgram() {
102
104
  printBanner();
103
105
  await runDoctor(options);
104
106
  });
105
- program.command("repair").description("Rebuild local Snipara config and re-run validation").option("--api-key <key>", "Snipara API key override").option("--slug <slug>", "Project slug override").option("--project-id <id>", "Project ID override").option("--profile <profile>", "Target install profile").option("--with-hooks", "Force hook generation").option("--with-runtime", "Force Snipara Sandbox server/config").option("--with-mcp", "Force MCP config").option("--with-companion", "Force companion pack generation").action(async (options) => {
107
+ program.command("repair").description("Rebuild local Snipara config and re-run validation").option("--api-key <key>", "Snipara API key override").option("--slug <slug>", "Project slug override").option("--project-id <id>", "Project ID override").option("--profile <profile>", "Target install profile").option("--with-hooks", "Force hook generation").option("--with-runtime", "Force Snipara Sandbox server/config").option("--with-mcp", "Force MCP config").option("--with-companion", "Force companion pack generation").option("--with-orchestrator", "Install optional snipara-orchestrator package").action(async (options) => {
106
108
  printBanner();
107
109
  await runRepair(options);
108
110
  });
109
- program.command("upgrade").description("Upgrade installed Snipara local components").option("--with-hooks", "Refresh hooks after upgrade").option("--with-runtime", "Upgrade Snipara Sandbox even if not detected").option("--with-mcp", "Upgrade snipara-mcp even if not detected").option("--with-companion", "Upgrade companion even if not detected").action(async (options) => {
111
+ program.command("upgrade").description("Upgrade installed Snipara local components").option("--with-hooks", "Refresh hooks after upgrade").option("--with-runtime", "Upgrade Snipara Sandbox even if not detected").option("--with-mcp", "Upgrade snipara-mcp even if not detected").option("--with-companion", "Upgrade companion even if not detected").option("--with-orchestrator", "Upgrade snipara-orchestrator even if not detected").action(async (options) => {
110
112
  printBanner();
111
113
  await runUpgrade(options);
112
114
  });
@@ -125,7 +127,7 @@ function addSharedRootOptions(program) {
125
127
  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(
126
128
  "--profile <profile>",
127
129
  "Install profile: hosted-only, hosted-companion, full-stack, runtime-only"
128
- ).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("--github", "Open GitHub repository automation setup when a git remote is detected").option("--skip-github", "Skip the GitHub automation prompt").option("--github-path <path>", "Optional GitHub path filter for the initial repository sync").option("--runtime-only", "Install only Snipara Sandbox").option("-y, --yes", "Accept defaults without prompting");
130
+ ).option("--skip-companion", "Skip local companion CLI installation").option("--skip-mcp", "Skip local snipara-mcp package installation").option("--skip-runtime", "Skip snipara-sandbox installation").option("--skip-hooks", "Skip hook generation").option("--skip-test", "Skip hosted connection test").option("--with-orchestrator", "Install optional snipara-orchestrator package").option("--github", "Open GitHub repository automation setup when a git remote is detected").option("--skip-github", "Skip the GitHub automation prompt").option("--github-path <path>", "Optional GitHub path filter for the initial repository sync").option("--runtime-only", "Install only Snipara Sandbox").option("-y, --yes", "Accept defaults without prompting");
129
131
  }
130
132
  async function main() {
131
133
  const program = buildProgram();
@@ -157,6 +159,7 @@ function parseInitOptionsFromArgv(args) {
157
159
  "--skip-runtime": "skipRuntime",
158
160
  "--skip-hooks": "skipHooks",
159
161
  "--skip-test": "skipTest",
162
+ "--with-orchestrator": "withOrchestrator",
160
163
  "--runtime-only": "runtimeOnly",
161
164
  "--github": "github",
162
165
  "--skip-github": "skipGithub",
@@ -187,7 +190,7 @@ function parseInitOptionsFromArgv(args) {
187
190
  }
188
191
  async function runInit(options) {
189
192
  const answers = await gatherConfiguration(normalizeInitOptions(options));
190
- if (answers.installMcp || answers.installRuntime) {
193
+ if (answers.installMcp || answers.installRuntime || answers.installOrchestrator) {
191
194
  await installPythonPackages(answers, false);
192
195
  }
193
196
  if (answers.installCompanion) {
@@ -201,6 +204,7 @@ async function runInit(options) {
201
204
  await installClaudeCodeHooks(answers);
202
205
  }
203
206
  await updateEnvFiles(answers);
207
+ await createCompanionConfig(answers);
204
208
  await createCompanionPack(answers);
205
209
  const validationReport = await runValidation(answers, !options.skipTest);
206
210
  writeDoctorReport(validationReport);
@@ -225,7 +229,7 @@ async function runDoctor(options) {
225
229
  async function runRepair(options) {
226
230
  const answers = loadExistingAnswers(options);
227
231
  console.log(chalk.bold("\n\u{1F6E0}\uFE0F Repairing Local Setup\n"));
228
- if (answers.installMcp || answers.installRuntime) {
232
+ if (answers.installMcp || answers.installRuntime || answers.installOrchestrator) {
229
233
  await installPythonPackages(answers, true);
230
234
  }
231
235
  if (answers.installCompanion) {
@@ -234,6 +238,7 @@ async function runRepair(options) {
234
238
  await createMcpConfig(answers);
235
239
  await createAgentTemplates(answers);
236
240
  await updateEnvFiles(answers);
241
+ await createCompanionConfig(answers);
237
242
  await createCompanionPack(answers);
238
243
  if (answers.installHooks && answers.aiClient === "claude-code") {
239
244
  await installClaudeCodeHooks(answers);
@@ -248,10 +253,11 @@ async function runUpgrade(options) {
248
253
  const upgradedAnswers = {
249
254
  ...answers,
250
255
  installMcp: options.withMcp || answers.installMcp,
251
- installRuntime: options.withRuntime || answers.installRuntime
256
+ installRuntime: options.withRuntime || answers.installRuntime,
257
+ installOrchestrator: options.withOrchestrator || answers.installOrchestrator
252
258
  };
253
259
  console.log(chalk.bold("\n\u2B06\uFE0F Upgrading Local Components\n"));
254
- if (options.withMcp || options.withRuntime || detection.existingConfig.hasRuntimeServer) {
260
+ if (options.withMcp || options.withRuntime || options.withOrchestrator || detection.existingConfig.hasRuntimeServer || detection.tooling.orchestratorCli) {
255
261
  await installPythonPackages(upgradedAnswers, true);
256
262
  }
257
263
  if (options.withCompanion || detection.tooling.companionCli || answers.installCompanion) {
@@ -261,11 +267,12 @@ async function runUpgrade(options) {
261
267
  await createMcpConfig(upgradedAnswers);
262
268
  }
263
269
  await createAgentTemplates(upgradedAnswers);
270
+ await createCompanionConfig(upgradedAnswers);
264
271
  if (options.withHooks || answers.installHooks) {
265
272
  await installClaudeCodeHooks({ ...answers, installHooks: true });
266
273
  }
267
- await createCompanionPack(answers);
268
- const report = await runValidation(answers, true);
274
+ await createCompanionPack(upgradedAnswers);
275
+ const report = await runValidation(upgradedAnswers, true);
269
276
  writeDoctorReport(report);
270
277
  printDoctorReport(detectEnvironment(), report);
271
278
  }
@@ -302,6 +309,7 @@ async function runPrintConfig(options) {
302
309
  );
303
310
  console.log(` Local snipara-mcp package: ${answers.installMcp ? "enabled" : "disabled"}`);
304
311
  console.log(` Snipara Sandbox: ${answers.installRuntime ? "enabled" : "disabled"}`);
312
+ console.log(` Snipara Orchestrator: ${answers.installOrchestrator ? "enabled" : "disabled"}`);
305
313
  console.log(` Hooks: ${answers.installHooks ? "enabled" : "disabled"}`);
306
314
  console.log(` GitHub repo: ${githubRepo ? githubRepo.fullName : "not detected"}`);
307
315
  console.log(` ${GITHUB_ANSWER_PACKS_LABEL}: ${githubAnswerPacks}`);
@@ -362,6 +370,7 @@ async function gatherConfiguration(options) {
362
370
  skipCompanion: !!options.skipCompanion,
363
371
  skipMcp: !!options.skipMcp,
364
372
  skipRuntime: !!options.skipRuntime,
373
+ withOrchestrator: !!options.withOrchestrator,
365
374
  skipHooks: !!options.skipHooks,
366
375
  configureGitHub: Boolean(options.github && detectedGitRepo && !options.skipGithub),
367
376
  githubRepo: detectedGitRepo || void 0,
@@ -426,6 +435,12 @@ async function gatherConfiguration(options) {
426
435
  ),
427
436
  choices: AI_CLIENT_CHOICES
428
437
  },
438
+ {
439
+ type: showAdvancedPrompts ? "confirm" : null,
440
+ name: "installOrchestrator",
441
+ message: "Install snipara-orchestrator for production gates, drift checks, or htask workflows?",
442
+ initial: Boolean(options.withOrchestrator)
443
+ },
429
444
  {
430
445
  type: showAdvancedPrompts ? "confirm" : null,
431
446
  name: "installHooks",
@@ -471,6 +486,7 @@ async function gatherConfiguration(options) {
471
486
  skipCompanion: !!options.skipCompanion,
472
487
  skipMcp: !!options.skipMcp,
473
488
  skipRuntime: !!options.skipRuntime,
489
+ withOrchestrator: Boolean(options.withOrchestrator || response.installOrchestrator),
474
490
  skipHooks: !!options.skipHooks || !response.installHooks,
475
491
  configureGitHub: Boolean(
476
492
  (response.configureGitHub || options.github) && detectedGitRepo && !options.skipGithub
@@ -602,6 +618,7 @@ function buildAnswersFromProfile(input) {
602
618
  installCompanion: selected.installCompanion && !input.skipCompanion,
603
619
  installMcp: selected.installMcp && !input.skipMcp,
604
620
  installRuntime: selected.installRuntime && !input.skipRuntime,
621
+ installOrchestrator: input.withOrchestrator,
605
622
  installHooks: selected.installHooks && !input.skipHooks,
606
623
  aiClient: input.aiClient,
607
624
  configureGitHub: Boolean(input.configureGitHub && input.githubRepo),
@@ -756,6 +773,9 @@ function detectSandboxCommand() {
756
773
  }
757
774
  return null;
758
775
  }
776
+ function detectOrchestratorCommand() {
777
+ return commandExists(ORCHESTRATOR_COMMAND_NAME) ? ORCHESTRATOR_COMMAND_NAME : null;
778
+ }
759
779
  async function installPythonPackages(answers, upgrade) {
760
780
  console.log(chalk.bold(`
761
781
  \u{1F4E6} ${upgrade ? "Upgrading" : "Installing"} Python Packages
@@ -769,6 +789,11 @@ async function installPythonPackages(answers, upgrade) {
769
789
  if (answers.installRuntime) {
770
790
  console.log(chalk.gray(` pip install ${upgrade ? "-U " : ""}${SANDBOX_PACKAGE_NAME}`));
771
791
  }
792
+ if (answers.installOrchestrator) {
793
+ console.log(
794
+ chalk.gray(` pip install ${upgrade ? "-U " : ""}${ORCHESTRATOR_PACKAGE_NAME}`)
795
+ );
796
+ }
772
797
  console.log("");
773
798
  return;
774
799
  }
@@ -779,6 +804,9 @@ async function installPythonPackages(answers, upgrade) {
779
804
  if (answers.installRuntime) {
780
805
  packages.push(SANDBOX_PACKAGE_NAME);
781
806
  }
807
+ if (answers.installOrchestrator) {
808
+ packages.push(ORCHESTRATOR_PACKAGE_NAME);
809
+ }
782
810
  if (packages.length === 0) return;
783
811
  const spinner = ora(
784
812
  `${upgrade ? "Upgrading" : "Installing"} ${packages.map(stripExtras).join(", ")}...`
@@ -1154,11 +1182,10 @@ async function createClaudeDesktopConfig(answers) {
1154
1182
  }
1155
1183
  config.mcpServers = config.mcpServers || {};
1156
1184
  config.mcpServers.snipara = {
1157
- command: "uvx",
1158
- args: [MCP_PACKAGE_NAME],
1159
- env: {
1160
- SNIPARA_API_KEY: answers.apiKey,
1161
- SNIPARA_PROJECT_SLUG: answers.projectSlug
1185
+ type: "http",
1186
+ url: buildHostedMcpEndpoint(answers),
1187
+ headers: {
1188
+ "X-API-Key": answers.apiKey
1162
1189
  }
1163
1190
  };
1164
1191
  if (answers.installRuntime) {
@@ -1351,12 +1378,19 @@ async function updateEnvFiles(answers) {
1351
1378
  const envLocalPath = path.join(process.cwd(), ".env.local");
1352
1379
  const envPath = path.join(process.cwd(), ".env");
1353
1380
  let envVars = "";
1381
+ let envExampleVars = "";
1354
1382
  if (answers.apiKeyType !== "none") {
1355
1383
  envVars += `
1356
1384
  # Snipara Configuration
1357
1385
  SNIPARA_API_KEY=${answers.apiKey.includes("YOUR_") ? "your_api_key_here" : answers.apiKey}
1358
1386
  SNIPARA_PROJECT_SLUG=${answers.projectSlug}
1359
1387
  ${answers.projectId ? `SNIPARA_PROJECT_ID=${answers.projectId}` : ""}
1388
+ `.trim();
1389
+ envExampleVars += `
1390
+ # Snipara Configuration
1391
+ SNIPARA_API_KEY=your_api_key_here
1392
+ SNIPARA_PROJECT_SLUG=${answers.projectSlug || "your_project_slug"}
1393
+ ${answers.projectId ? `SNIPARA_PROJECT_ID=${answers.projectId}` : "SNIPARA_PROJECT_ID=your_project_id"}
1360
1394
  `.trim();
1361
1395
  }
1362
1396
  if (answers.llmProvider && answers.llmProvider !== "none" && answers.llmApiKey) {
@@ -1365,23 +1399,28 @@ ${answers.projectId ? `SNIPARA_PROJECT_ID=${answers.projectId}` : ""}
1365
1399
 
1366
1400
  # Snipara Sandbox LLM Provider (for snipara-sandbox run/agent CLI)
1367
1401
  ${llmVarName}=${answers.llmApiKey}`;
1402
+ envExampleVars += `
1403
+
1404
+ # Snipara Sandbox LLM Provider (for snipara-sandbox run/agent CLI)
1405
+ ${llmVarName}=your_${answers.llmProvider}_api_key_here`;
1368
1406
  }
1369
1407
  envVars = envVars.trim();
1370
- if (!envVars) {
1408
+ envExampleVars = envExampleVars.trim();
1409
+ if (!envVars && !envExampleVars) {
1371
1410
  console.log(chalk.dim(" No environment variables to save"));
1372
1411
  return;
1373
1412
  }
1374
- if (fs.existsSync(envExamplePath)) {
1413
+ if (envExampleVars && fs.existsSync(envExamplePath)) {
1375
1414
  const content = fs.readFileSync(envExamplePath, "utf-8");
1376
1415
  if (!content.includes("SNIPARA_API_KEY") && !content.includes("SNIPARA_PROJECT_SLUG")) {
1377
1416
  fs.appendFileSync(envExamplePath, `
1378
1417
 
1379
- ${envVars}
1418
+ ${envExampleVars}
1380
1419
  `);
1381
1420
  console.log(chalk.green(" \u2713 Updated .env.example"));
1382
1421
  }
1383
- } else {
1384
- fs.writeFileSync(envExamplePath, envVars + "\n");
1422
+ } else if (envExampleVars) {
1423
+ fs.writeFileSync(envExamplePath, envExampleVars + "\n");
1385
1424
  console.log(chalk.green(" \u2713 Created .env.example"));
1386
1425
  }
1387
1426
  const targetEnvPath = fs.existsSync(envLocalPath) ? envLocalPath : envPath;
@@ -1401,6 +1440,26 @@ ${envVars}
1401
1440
  console.log(chalk.green(` \u2713 Created ${path.basename(targetEnvPath)}`));
1402
1441
  }
1403
1442
  }
1443
+ async function createCompanionConfig(answers) {
1444
+ if (!answers.installCompanion || answers.apiKeyType === "none" || !answers.apiKey || answers.apiKey.includes("YOUR_")) {
1445
+ return;
1446
+ }
1447
+ const configDir = path.join(process.cwd(), ".snipara", "companion");
1448
+ const configPath = path.join(configDir, "config.json");
1449
+ fs.mkdirSync(configDir, { recursive: true });
1450
+ const config = {
1451
+ apiKey: answers.apiKey,
1452
+ projectId: answers.projectId || answers.projectSlug,
1453
+ apiUrl: API_URL,
1454
+ sessionId: `sess_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
1455
+ };
1456
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
1457
+ ensureGitignoreEntry(
1458
+ ".snipara/companion/config.json",
1459
+ "# Snipara local companion config (contains API key)"
1460
+ );
1461
+ console.log(chalk.green(" \u2713 Configured local companion workspace"));
1462
+ }
1404
1463
  function getClientGuidance(client) {
1405
1464
  if (client === "claude-code") {
1406
1465
  return [
@@ -1463,7 +1522,6 @@ function getClientGuidance(client) {
1463
1522
  }
1464
1523
  function getCompanionCommands(answers) {
1465
1524
  const projectIdFlag = answers.projectId ? ` --project-id ${answers.projectId}` : "";
1466
- const apiKeyFlag = " --api-key <snipara-key>";
1467
1525
  const clientFlag = getCompanionInitClientFlag(answers.aiClient);
1468
1526
  const businessOnboardingCommands = answers.installCompanion ? [
1469
1527
  {
@@ -1495,8 +1553,8 @@ function getCompanionCommands(answers) {
1495
1553
  return [
1496
1554
  {
1497
1555
  title: "Bootstrap local companion",
1498
- command: `${COMPANION_COMMAND_NAME} init${clientFlag}${projectIdFlag}${apiKeyFlag}`,
1499
- purpose: "Write local companion config for this workstation."
1556
+ command: `${COMPANION_COMMAND_NAME} init${clientFlag}${projectIdFlag}`,
1557
+ purpose: "Re-run local companion setup if this workstation needs to refresh browser login or project selection."
1500
1558
  },
1501
1559
  {
1502
1560
  title: "Run doctor for this project",
@@ -1538,6 +1596,11 @@ function getCompanionCommands(answers) {
1538
1596
  command: `${COMPANION_COMMAND_NAME} code impact --changed-files src/api/webhooks.ts tests/webhooks.test.ts --diff-summary "webhook retry hardening"`,
1539
1597
  purpose: "Use paid Context code impact analysis to surface likely tests, docs, routes, config, and risks."
1540
1598
  },
1599
+ {
1600
+ title: answers.installOrchestrator ? "Run an explicit production orchestration gate" : "Enable explicit production orchestration",
1601
+ command: answers.installOrchestrator ? `${ORCHESTRATOR_COMMAND_NAME} check-drift --route /api/health` : "npx create-snipara repair --with-orchestrator",
1602
+ purpose: answers.installOrchestrator ? "Use snipara-orchestrator only for proof-based validation, drift checks, htasks, or multi-agent coordination." : "Install snipara-orchestrator only when production gates, drift checks, htasks, or multi-agent coordination are required."
1603
+ },
1541
1604
  githubAnswerPacksCommand,
1542
1605
  {
1543
1606
  title: "Upload or refresh a doc",
@@ -1715,6 +1778,7 @@ This project uses Snipara as a hosted-first context and memory layer with local
1715
1778
  - Generated hosted MCP config: \`${answers.profile !== "runtime-only" ? "yes" : "no"}\`
1716
1779
  - Installed local snipara-mcp package: \`${answers.installMcp ? "yes" : "no"}\`
1717
1780
  - Installed Snipara Sandbox: \`${answers.installRuntime ? "yes" : "no"}\`
1781
+ - Installed Snipara Orchestrator: \`${answers.installOrchestrator ? "yes" : "no"}\`
1718
1782
  - Generated hooks: \`${answers.installHooks ? "yes" : "no"}\`
1719
1783
  - GitHub repository: \`${answers.githubRepo ? answers.githubRepo.fullName : "not-detected"}\`
1720
1784
 
@@ -1725,6 +1789,7 @@ This project uses Snipara as a hosted-first context and memory layer with local
1725
1789
  - .env.local present: \`${detection.files.envLocal ? "yes" : "no"}\`
1726
1790
  - Companion CLI on PATH: \`${detection.tooling.companionCli ? "yes" : "no"}\`
1727
1791
  - Snipara Sandbox CLI on PATH: \`${detection.tooling.runtimeCli ? "yes" : "no"}\`
1792
+ - Snipara Orchestrator CLI on PATH: \`${detection.tooling.orchestratorCli ? "yes" : "no"}\`
1728
1793
 
1729
1794
  ## Client Guidance
1730
1795
 
@@ -1790,6 +1855,7 @@ ${item.purpose}`
1790
1855
  ai_client: answers.aiClient,
1791
1856
  profile: answers.profile,
1792
1857
  llm_provider: answers.llmProvider || null,
1858
+ install_orchestrator: answers.installOrchestrator,
1793
1859
  github_repository: answers.githubRepo || null,
1794
1860
  github_automation_url: answers.githubRepo ? buildGitHubAutomationUrl(answers) : null,
1795
1861
  client_pack: clientPackFilename,
@@ -1895,6 +1961,11 @@ async function runValidation(answers, includeHostedCheck) {
1895
1961
  status: answers.installRuntime ? detection.tooling.runtimeCli ? "pass" : "warn" : "pass",
1896
1962
  detail: answers.installRuntime && !detection.tooling.runtimeCli ? `${SANDBOX_COMMAND_NAME} CLI not found on PATH` : "Snipara Sandbox CLI ready or not requested"
1897
1963
  });
1964
+ checks.push({
1965
+ name: "orchestrator_cli",
1966
+ status: answers.installOrchestrator ? detection.tooling.orchestratorCli ? "pass" : "warn" : "pass",
1967
+ detail: answers.installOrchestrator && !detection.tooling.orchestratorCli ? `${ORCHESTRATOR_COMMAND_NAME} CLI not found on PATH` : "Snipara Orchestrator ready or not requested"
1968
+ });
1898
1969
  checks.push({
1899
1970
  name: "companion_pack",
1900
1971
  status: detection.files.sniparaCompanionPack ? "pass" : "warn",
@@ -2096,6 +2167,11 @@ function printSuccess(answers, report) {
2096
2167
  chalk.green(` \u2713 ${stripExtras(SANDBOX_PACKAGE_NAME)}`) + chalk.dim(" - Safe code execution")
2097
2168
  );
2098
2169
  }
2170
+ if (answers.installOrchestrator) {
2171
+ console.log(
2172
+ chalk.green(` \u2713 ${ORCHESTRATOR_PACKAGE_NAME}`) + chalk.dim(" - Production gates, drift checks, and htask workflows")
2173
+ );
2174
+ }
2099
2175
  if (answers.installCompanion) {
2100
2176
  console.log(
2101
2177
  chalk.green(` \u2713 ${COMPANION_PACKAGE_NAME}`) + chalk.dim(" - Local companion workflows and hook bootstrap")
@@ -2147,6 +2223,11 @@ function printSuccess(answers, report) {
2147
2223
  `Run ${SANDBOX_COMMAND_NAME} init when you want to choose sandbox/docker/local execution.`
2148
2224
  );
2149
2225
  }
2226
+ if (answers.installOrchestrator) {
2227
+ printNextStep(
2228
+ `Use ${ORCHESTRATOR_COMMAND_NAME} explicitly for production proof gates, drift checks, or htask coordination.`
2229
+ );
2230
+ }
2150
2231
  if (answers.githubRepo) {
2151
2232
  printNextStep(
2152
2233
  `Connect GitHub automation for ${answers.githubRepo.fullName} from the generated Snipara link.`
@@ -2213,7 +2294,8 @@ function detectEnvironment() {
2213
2294
  docker: commandExists("docker"),
2214
2295
  git: commandExists("git"),
2215
2296
  companionCli: Boolean(detectCompanionCommand()),
2216
- runtimeCli: Boolean(detectSandboxCommand())
2297
+ runtimeCli: Boolean(detectSandboxCommand()),
2298
+ orchestratorCli: Boolean(detectOrchestratorCommand())
2217
2299
  },
2218
2300
  existingConfig: {
2219
2301
  slug: existingConfig?.slug,
@@ -2243,6 +2325,7 @@ function loadExistingAnswers(options) {
2243
2325
  skipCompanion: options.withCompanion === true ? false : !detection.tooling.companionCli && effectiveProfile !== "hosted-companion" && effectiveProfile !== "full-stack",
2244
2326
  skipMcp: options.withMcp === true ? false : !detection.existingConfig.hasSniparaServer && effectiveProfile === "runtime-only",
2245
2327
  skipRuntime: options.withRuntime === true ? false : !detection.existingConfig.hasRuntimeServer && effectiveProfile !== "full-stack" && effectiveProfile !== "runtime-only",
2328
+ withOrchestrator: Boolean(options.withOrchestrator || detection.tooling.orchestratorCli),
2246
2329
  skipHooks: options.withHooks ? false : detection.aiClient !== "claude-code"
2247
2330
  });
2248
2331
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-snipara",
3
- "version": "1.3.17",
3
+ "version": "1.3.19",
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",