create-snipara 1.3.11 → 1.3.13
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 +43 -39
- package/dist/index.js +252 -159
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -50,8 +50,8 @@ The product surfaces remain:
|
|
|
50
50
|
|
|
51
51
|
The default CLI path wires the local companion around those surfaces:
|
|
52
52
|
|
|
53
|
-
- `snipara-companion` for local
|
|
54
|
-
- `
|
|
53
|
+
- `snipara-companion` for local workflow commands and managed phase commits; legacy `rlm-hook` commands remain compatible
|
|
54
|
+
- `snipara-sandbox` for optional local code execution
|
|
55
55
|
- local config, env files, hook scripts, and a project companion pack
|
|
56
56
|
- `snipara-mcp` only for local stdio compatibility, development, or testing
|
|
57
57
|
|
|
@@ -59,12 +59,12 @@ The default CLI path wires the local companion around those surfaces:
|
|
|
59
59
|
|
|
60
60
|
The CLI now works around explicit install profiles:
|
|
61
61
|
|
|
62
|
-
| Profile | What it installs
|
|
63
|
-
| ------------------ |
|
|
64
|
-
| `hosted-companion` | MCP + `snipara-companion`
|
|
65
|
-
| `hosted-only` | Hosted MCP config only
|
|
66
|
-
| `full-stack` | MCP + `snipara-companion` + `
|
|
67
|
-
| `runtime-only` | `
|
|
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 |
|
|
68
68
|
|
|
69
69
|
Default profile: `hosted-companion`
|
|
70
70
|
|
|
@@ -76,7 +76,7 @@ Free onboarding is now **Context + Memory**, not Context-only:
|
|
|
76
76
|
- Memory Free: reviewed hosted memories for continuity across sessions
|
|
77
77
|
- Hosted MCP: direct connection for Claude Code, Codex, Cursor, ChatGPT Desktop, and other MCP clients
|
|
78
78
|
|
|
79
|
-
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
|
|
79
|
+
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.
|
|
80
80
|
|
|
81
81
|
## The Two Approval Paths
|
|
82
82
|
|
|
@@ -100,7 +100,7 @@ That distinction matters:
|
|
|
100
100
|
- writes Hosted MCP configuration
|
|
101
101
|
- detects a GitHub remote and can open the Snipara GitHub App automation path
|
|
102
102
|
- installs `snipara-companion` by default for local managed workflows
|
|
103
|
-
- installs optional `
|
|
103
|
+
- installs optional `snipara-sandbox`
|
|
104
104
|
- configures `.mcp.json`
|
|
105
105
|
- generates merge-ready AI agent templates in `.snipara/templates/`, including Pro+ symbol-card, code-impact, and managed workflow guidance for paid Context plans
|
|
106
106
|
- creates `AGENTS.md`, `CLAUDE.md`, `.cursor/rules/snipara.mdc`, and `.codex/config.toml` when those files do not already exist
|
|
@@ -131,13 +131,13 @@ npx create-snipara
|
|
|
131
131
|
The interactive flow now asks for:
|
|
132
132
|
|
|
133
133
|
- project slug
|
|
134
|
-
- API key path:
|
|
134
|
+
- API key path: existing API key, free signup, or add key later
|
|
135
135
|
- AI client
|
|
136
136
|
|
|
137
137
|
By default, this uses the recommended `hosted-companion` setup: hosted MCP plus
|
|
138
|
-
the local `
|
|
139
|
-
|
|
140
|
-
provider prompts.
|
|
138
|
+
the local `snipara-companion` workflow helper. Legacy `rlm-hook` commands remain
|
|
139
|
+
compatible. Add `--advanced` when you want profile selection, project id entry,
|
|
140
|
+
hook prompts, GitHub automation prompts, or sandbox provider prompts.
|
|
141
141
|
|
|
142
142
|
The advanced flow asks for:
|
|
143
143
|
|
|
@@ -145,7 +145,7 @@ The advanced flow asks for:
|
|
|
145
145
|
- optional project id
|
|
146
146
|
- whether hooks should be generated
|
|
147
147
|
- whether to open GitHub repository automation when a GitHub remote is detected
|
|
148
|
-
- optional
|
|
148
|
+
- optional sandbox provider config when `snipara-sandbox` is installed
|
|
149
149
|
|
|
150
150
|
The generated agent templates cover:
|
|
151
151
|
|
|
@@ -158,12 +158,12 @@ The generated agent templates cover:
|
|
|
158
158
|
When `snipara-companion` is installed, the companion pack also includes the compaction-safe managed workflow:
|
|
159
159
|
|
|
160
160
|
```bash
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
snipara-companion workflow start --goal "ship auth hardening" --plan-file ./plan.md
|
|
162
|
+
snipara-companion workflow phase-start context
|
|
163
|
+
snipara-companion workflow run --mode full --include-session-context --query "load auth context"
|
|
164
|
+
snipara-companion code impact --changed-files src/auth.ts tests/auth.test.ts --diff-summary "auth hardening"
|
|
165
|
+
snipara-companion workflow phase-commit context --summary "Loaded context and mapped impacted files" --files src/auth.ts
|
|
166
|
+
snipara-companion final-commit --summary "Shipped auth hardening and tests" --files src/auth.ts tests/auth.test.ts
|
|
167
167
|
```
|
|
168
168
|
|
|
169
169
|
## Main Commands
|
|
@@ -187,7 +187,7 @@ Writes `.snipara/companion/doctor.json` and validates:
|
|
|
187
187
|
|
|
188
188
|
- local config files
|
|
189
189
|
- companion CLI presence
|
|
190
|
-
-
|
|
190
|
+
- Snipara Sandbox CLI presence
|
|
191
191
|
- environment files
|
|
192
192
|
- hosted MCP reachability when a live API key is available
|
|
193
193
|
|
|
@@ -264,11 +264,11 @@ npx create-snipara --profile hosted-only
|
|
|
264
264
|
npx create-snipara --profile full-stack
|
|
265
265
|
npx create-snipara --advanced
|
|
266
266
|
|
|
267
|
-
#
|
|
267
|
+
# Sandbox-only local execution
|
|
268
268
|
npx create-snipara --runtime-only
|
|
269
269
|
|
|
270
|
-
# Non-interactive setup
|
|
271
|
-
npx create-snipara -y --api-key
|
|
270
|
+
# Non-interactive setup with an existing API key
|
|
271
|
+
npx create-snipara -y --api-key snp-xxx --slug my-project
|
|
272
272
|
|
|
273
273
|
# Open GitHub automation after setup when a GitHub remote is detected
|
|
274
274
|
npx create-snipara --github
|
|
@@ -292,9 +292,9 @@ npx create-snipara upgrade --with-runtime
|
|
|
292
292
|
The intended split is:
|
|
293
293
|
|
|
294
294
|
- `create-snipara`: Context + Memory onboarding, auth, config, inspect, repair, and upgrade
|
|
295
|
-
- `snipara-companion`: default local workflow helper
|
|
295
|
+
- `snipara-companion`: default local workflow helper; legacy `rlm-hook` commands remain compatible
|
|
296
296
|
- `snipara-mcp`: local stdio bridge, client-package development, compatibility, and testing
|
|
297
|
-
- `
|
|
297
|
+
- `snipara-sandbox`: optional local execution engine
|
|
298
298
|
|
|
299
299
|
## Codex Setup
|
|
300
300
|
|
|
@@ -324,8 +324,8 @@ This pack is meant to be the local source of truth for machine-level setup. When
|
|
|
324
324
|
such as:
|
|
325
325
|
|
|
326
326
|
```bash
|
|
327
|
-
|
|
328
|
-
|
|
327
|
+
snipara-companion onboard-folder ./client-export --source-provider local_folder --write-manifest ./snipara-onboard.json
|
|
328
|
+
snipara-companion onboard-folder ./client-export --source-provider local_folder --apply
|
|
329
329
|
```
|
|
330
330
|
|
|
331
331
|
Use this for dashboardless business-context imports after an LLM client has
|
|
@@ -356,13 +356,17 @@ Recommended explanation to users:
|
|
|
356
356
|
|
|
357
357
|
## API Key Requirements
|
|
358
358
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
|
364
|
-
|
|
|
365
|
-
|
|
|
359
|
+
Existing manual API keys passed to `create-snipara --api-key` should start with `snp-`;
|
|
360
|
+
legacy `rlm_` keys remain accepted for migration. When `create-snipara` provisions a
|
|
361
|
+
new account through the browser flow, Snipara returns a new `snp-` key from the current hosted API.
|
|
362
|
+
|
|
363
|
+
| Tooling | Snipara API key | LLM API key |
|
|
364
|
+
| --------------------------------------------------- | -------------------------- | ----------- |
|
|
365
|
+
| Hosted MCP queries | Required | Not needed |
|
|
366
|
+
| Memory / planning tools | Required with memory scope | Not needed |
|
|
367
|
+
| `snipara-companion` hosted workflows | Optional | Not needed |
|
|
368
|
+
| `snipara-sandbox` `execute_python` MCP | Not needed | Not needed |
|
|
369
|
+
| `snipara-sandbox run` / `snipara-sandbox agent` CLI | Optional for context | Required |
|
|
366
370
|
|
|
367
371
|
## Example Flows
|
|
368
372
|
|
|
@@ -372,7 +376,7 @@ Recommended explanation to users:
|
|
|
372
376
|
npx create-snipara
|
|
373
377
|
```
|
|
374
378
|
|
|
375
|
-
Best when the user wants Hosted MCP Context + Memory plus `
|
|
379
|
+
Best when the user wants Hosted MCP Context + Memory plus `snipara-companion` managed workflows.
|
|
376
380
|
|
|
377
381
|
### Minimal hosted setup
|
|
378
382
|
|
|
@@ -388,7 +392,7 @@ Best when the user only wants Hosted MCP Context + Memory and cannot install loc
|
|
|
388
392
|
npx create-snipara --profile full-stack --advanced
|
|
389
393
|
```
|
|
390
394
|
|
|
391
|
-
Best when the user also wants sandboxed `
|
|
395
|
+
Best when the user also wants sandboxed `snipara-sandbox` execution.
|
|
392
396
|
|
|
393
397
|
### Connect the current repository
|
|
394
398
|
|
|
@@ -433,7 +437,7 @@ npx create-snipara print-config --json
|
|
|
433
437
|
When testing a locally packed tarball, use `npm exec --package` instead of `npx`, for example:
|
|
434
438
|
|
|
435
439
|
```bash
|
|
436
|
-
npm exec --package ./create-snipara-1.3.
|
|
440
|
+
npm exec --package ./create-snipara-1.3.13.tgz create-snipara -- --help
|
|
437
441
|
```
|
|
438
442
|
|
|
439
443
|
This avoids `npx` resolution issues with unpublished tarballs.
|
package/dist/index.js
CHANGED
|
@@ -9,12 +9,18 @@ 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.
|
|
12
|
+
var VERSION = "1.3.13";
|
|
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";
|
|
16
|
+
var COMPANION_COMMAND_NAME = "snipara-companion";
|
|
17
|
+
var LEGACY_COMPANION_COMMAND_NAME = "rlm-hook";
|
|
16
18
|
var MCP_PACKAGE_NAME = "snipara-mcp";
|
|
17
|
-
var
|
|
19
|
+
var SANDBOX_PACKAGE_NAME = "snipara-sandbox[all]";
|
|
20
|
+
var SANDBOX_SERVER_NAME = "snipara-sandbox";
|
|
21
|
+
var LEGACY_RUNTIME_SERVER_NAME = "rlm-runtime";
|
|
22
|
+
var SANDBOX_COMMAND_NAME = "snipara-sandbox";
|
|
23
|
+
var LEGACY_SANDBOX_COMMAND_NAME = "rlm";
|
|
18
24
|
var DEVICE_CLIENT_ID = "create-snipara";
|
|
19
25
|
var DeviceAuthorizationAborted = class extends Error {
|
|
20
26
|
};
|
|
@@ -27,10 +33,10 @@ function getCompanionInitClientFlag(aiClient) {
|
|
|
27
33
|
function buildProgram() {
|
|
28
34
|
const program = new Command();
|
|
29
35
|
program.name("create-snipara").description("Scaffold hosted Snipara MCP config plus companion workflow helpers").version(VERSION);
|
|
30
|
-
program.command("init").description("Install and configure Snipara for this project").option("--api-key <key>", "Snipara
|
|
36
|
+
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(
|
|
31
37
|
"--profile <profile>",
|
|
32
38
|
"Install profile: hosted-only, hosted-companion, full-stack, runtime-only"
|
|
33
|
-
).option("--skip-companion", "Skip local companion CLI installation").option("--skip-mcp", "Skip local snipara-mcp package installation").option("--skip-runtime", "Skip
|
|
39
|
+
).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) => {
|
|
34
40
|
printBanner();
|
|
35
41
|
await runInit(options);
|
|
36
42
|
});
|
|
@@ -38,11 +44,11 @@ function buildProgram() {
|
|
|
38
44
|
printBanner();
|
|
39
45
|
await runDoctor(options);
|
|
40
46
|
});
|
|
41
|
-
program.command("repair").description("Rebuild local Snipara config and re-run validation").option("--api-key <key>", "Snipara
|
|
47
|
+
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) => {
|
|
42
48
|
printBanner();
|
|
43
49
|
await runRepair(options);
|
|
44
50
|
});
|
|
45
|
-
program.command("upgrade").description("Upgrade installed Snipara local components").option("--with-hooks", "Refresh hooks after upgrade").option("--with-runtime", "Upgrade
|
|
51
|
+
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) => {
|
|
46
52
|
printBanner();
|
|
47
53
|
await runUpgrade(options);
|
|
48
54
|
});
|
|
@@ -58,10 +64,10 @@ function buildProgram() {
|
|
|
58
64
|
return program;
|
|
59
65
|
}
|
|
60
66
|
function addSharedRootOptions(program) {
|
|
61
|
-
program.option("--api-key <key>", "Snipara
|
|
67
|
+
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(
|
|
62
68
|
"--profile <profile>",
|
|
63
69
|
"Install profile: hosted-only, hosted-companion, full-stack, runtime-only"
|
|
64
|
-
).option("--skip-companion", "Skip local companion CLI installation").option("--skip-mcp", "Skip local snipara-mcp package installation").option("--skip-runtime", "Skip
|
|
70
|
+
).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");
|
|
65
71
|
}
|
|
66
72
|
async function main() {
|
|
67
73
|
const program = buildProgram();
|
|
@@ -81,7 +87,6 @@ function parseInitOptionsFromArgv(args) {
|
|
|
81
87
|
const parsed = {};
|
|
82
88
|
const valueFlags = {
|
|
83
89
|
"--api-key": "apiKey",
|
|
84
|
-
"--team-key": "teamKey",
|
|
85
90
|
"--slug": "slug",
|
|
86
91
|
"--project-id": "projectId",
|
|
87
92
|
"--profile": "profile",
|
|
@@ -145,8 +150,8 @@ async function runInit(options) {
|
|
|
145
150
|
if (answers.configureGitHub && answers.githubRepo) {
|
|
146
151
|
await openGitHubAutomation(answers);
|
|
147
152
|
}
|
|
148
|
-
if (answers.
|
|
149
|
-
await
|
|
153
|
+
if (answers.runSandboxInit && answers.installRuntime) {
|
|
154
|
+
await runSandboxInit();
|
|
150
155
|
}
|
|
151
156
|
}
|
|
152
157
|
async function runDoctor(options) {
|
|
@@ -223,7 +228,7 @@ async function runPrintConfig(options) {
|
|
|
223
228
|
` Hosted MCP config: ${answers.profile !== "runtime-only" ? "enabled" : "disabled"}`
|
|
224
229
|
);
|
|
225
230
|
console.log(` Local snipara-mcp package: ${answers.installMcp ? "enabled" : "disabled"}`);
|
|
226
|
-
console.log(`
|
|
231
|
+
console.log(` Snipara Sandbox: ${answers.installRuntime ? "enabled" : "disabled"}`);
|
|
227
232
|
console.log(` Hooks: ${answers.installHooks ? "enabled" : "disabled"}`);
|
|
228
233
|
console.log(` Existing .mcp.json: ${detection.files.mcpConfig ? "yes" : "no"}`);
|
|
229
234
|
console.log(` Companion pack: ${detection.files.sniparaCompanionPack ? "yes" : "no"}`);
|
|
@@ -239,7 +244,7 @@ function printBanner() {
|
|
|
239
244
|
\u2551 \u2022 Hosted MCP for LLM agents \u2551
|
|
240
245
|
\u2551 \u2022 Free account path with hosted context and reviewed memory \u2551
|
|
241
246
|
\u2551 \u2022 API / SDK for product integrations \u2551
|
|
242
|
-
\u2551 \u2022 Optional local companion and
|
|
247
|
+
\u2551 \u2022 Optional local companion and Snipara Sandbox helpers \u2551
|
|
243
248
|
\u2551 \u2022 One CLI for auth, config, doctor, repair, and upgrade \u2551
|
|
244
249
|
\u2551 \u2551
|
|
245
250
|
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
@@ -267,12 +272,16 @@ async function gatherConfiguration(options) {
|
|
|
267
272
|
detectedProjectId = existingConfig.projectId || "";
|
|
268
273
|
}
|
|
269
274
|
const initialProfile = resolveRequestedProfile(options);
|
|
275
|
+
const providedApiKey = options.apiKey?.trim();
|
|
276
|
+
if (providedApiKey && !isManualApiKey(providedApiKey)) {
|
|
277
|
+
throw new Error(manualApiKeyValidationMessage());
|
|
278
|
+
}
|
|
270
279
|
if (options.yes) {
|
|
271
280
|
return buildAnswersFromProfile({
|
|
272
281
|
projectSlug: options.slug || detectedSlug || "my-project",
|
|
273
282
|
projectId: options.projectId || detectedProjectId,
|
|
274
|
-
apiKey:
|
|
275
|
-
apiKeyType:
|
|
283
|
+
apiKey: providedApiKey || "YOUR_API_KEY_HERE",
|
|
284
|
+
apiKeyType: providedApiKey ? "api" : "none",
|
|
276
285
|
profile: initialProfile,
|
|
277
286
|
aiClient: detectedClient,
|
|
278
287
|
skipCompanion: !!options.skipCompanion,
|
|
@@ -306,19 +315,14 @@ async function gatherConfiguration(options) {
|
|
|
306
315
|
initial: options.projectId || detectedProjectId || ""
|
|
307
316
|
},
|
|
308
317
|
{
|
|
309
|
-
type: (_, values) => values.profile === "runtime-only" ||
|
|
318
|
+
type: (_, values) => values.profile === "runtime-only" || providedApiKey ? null : "select",
|
|
310
319
|
name: "apiKeyType",
|
|
311
|
-
message: "
|
|
320
|
+
message: "How do you want to add your Snipara API key?",
|
|
312
321
|
choices: [
|
|
313
322
|
{
|
|
314
|
-
title: "
|
|
315
|
-
value: "
|
|
316
|
-
description: "
|
|
317
|
-
},
|
|
318
|
-
{
|
|
319
|
-
title: "Team API key",
|
|
320
|
-
value: "team",
|
|
321
|
-
description: "Access to all projects in your team"
|
|
323
|
+
title: "I have an API key",
|
|
324
|
+
value: "api",
|
|
325
|
+
description: "Paste an API key that starts with snp- or legacy rlm_"
|
|
322
326
|
},
|
|
323
327
|
{
|
|
324
328
|
title: "Create free Context + Memory account",
|
|
@@ -332,10 +336,10 @@ async function gatherConfiguration(options) {
|
|
|
332
336
|
]
|
|
333
337
|
},
|
|
334
338
|
{
|
|
335
|
-
type: (_, values) => values.profile === "runtime-only" ? null : values.apiKeyType === "
|
|
339
|
+
type: (_, values) => values.profile === "runtime-only" ? null : values.apiKeyType === "api" ? "text" : null,
|
|
336
340
|
name: "apiKey",
|
|
337
|
-
message: (_, values) => values.apiKeyType === "
|
|
338
|
-
validate: (value) => value
|
|
341
|
+
message: (_, values) => values.apiKeyType === "api" ? "Enter your Snipara API key:" : "Enter your API key:",
|
|
342
|
+
validate: (value) => isManualApiKey(value) ? true : manualApiKeyValidationMessage()
|
|
339
343
|
},
|
|
340
344
|
{
|
|
341
345
|
type: "select",
|
|
@@ -392,17 +396,17 @@ async function gatherConfiguration(options) {
|
|
|
392
396
|
initial: options.githubPath || ""
|
|
393
397
|
}
|
|
394
398
|
]);
|
|
395
|
-
let apiKey =
|
|
396
|
-
let apiKeyType = response.profile === "runtime-only" ? "none" :
|
|
399
|
+
let apiKey = providedApiKey;
|
|
400
|
+
let apiKeyType = response.profile === "runtime-only" ? "none" : providedApiKey ? "api" : "none";
|
|
397
401
|
let signupResult = null;
|
|
398
402
|
if (!apiKey && response.profile !== "runtime-only") {
|
|
399
403
|
if (response.apiKeyType === "signup") {
|
|
400
404
|
signupResult = await handleSignup();
|
|
401
405
|
apiKey = signupResult.apiKey;
|
|
402
|
-
apiKeyType = "
|
|
403
|
-
} else if (response.apiKeyType === "
|
|
406
|
+
apiKeyType = "api";
|
|
407
|
+
} else if (response.apiKeyType === "api") {
|
|
404
408
|
apiKey = response.apiKey;
|
|
405
|
-
apiKeyType =
|
|
409
|
+
apiKeyType = "api";
|
|
406
410
|
} else {
|
|
407
411
|
apiKey = "YOUR_API_KEY_HERE";
|
|
408
412
|
apiKeyType = "none";
|
|
@@ -430,7 +434,7 @@ async function gatherConfiguration(options) {
|
|
|
430
434
|
{
|
|
431
435
|
type: "select",
|
|
432
436
|
name: "llmProvider",
|
|
433
|
-
message: "Will you use
|
|
437
|
+
message: "Will you use the Snipara Sandbox run/agent CLI too?",
|
|
434
438
|
choices: [
|
|
435
439
|
{
|
|
436
440
|
title: "No - execute_python only",
|
|
@@ -465,14 +469,14 @@ async function gatherConfiguration(options) {
|
|
|
465
469
|
},
|
|
466
470
|
{
|
|
467
471
|
type: "confirm",
|
|
468
|
-
name: "
|
|
469
|
-
message: "Run
|
|
472
|
+
name: "runSandboxInit",
|
|
473
|
+
message: "Run Snipara Sandbox init now to choose sandbox/docker/local?",
|
|
470
474
|
initial: false
|
|
471
475
|
}
|
|
472
476
|
]);
|
|
473
477
|
answers.llmProvider = runtimeResponse.llmProvider;
|
|
474
478
|
answers.llmApiKey = runtimeResponse.llmApiKey;
|
|
475
|
-
answers.
|
|
479
|
+
answers.runSandboxInit = runtimeResponse.runSandboxInit || false;
|
|
476
480
|
}
|
|
477
481
|
return answers;
|
|
478
482
|
}
|
|
@@ -498,17 +502,17 @@ function installProfileChoices() {
|
|
|
498
502
|
{
|
|
499
503
|
title: "Hosted only",
|
|
500
504
|
value: "hosted-only",
|
|
501
|
-
description: "Minimal setup: hosted MCP config, no local companion or
|
|
505
|
+
description: "Minimal setup: hosted MCP config, no local companion or sandbox"
|
|
502
506
|
},
|
|
503
507
|
{
|
|
504
|
-
title: "Hosted + Companion +
|
|
508
|
+
title: "Hosted + Companion + Sandbox",
|
|
505
509
|
value: "full-stack",
|
|
506
|
-
description: "Add
|
|
510
|
+
description: "Add Snipara Sandbox local execution as well"
|
|
507
511
|
},
|
|
508
512
|
{
|
|
509
|
-
title: "
|
|
513
|
+
title: "Sandbox only",
|
|
510
514
|
value: "runtime-only",
|
|
511
|
-
description: "
|
|
515
|
+
description: "Snipara Sandbox execution only, no hosted API key required"
|
|
512
516
|
}
|
|
513
517
|
];
|
|
514
518
|
}
|
|
@@ -582,8 +586,8 @@ async function handleManualSignup() {
|
|
|
582
586
|
const response = await prompts({
|
|
583
587
|
type: "text",
|
|
584
588
|
name: "apiKey",
|
|
585
|
-
message: "Paste your
|
|
586
|
-
validate: (value) => value
|
|
589
|
+
message: "Paste your API key when ready:",
|
|
590
|
+
validate: (value) => isProvisionedApiKey(value) ? true : provisionedApiKeyValidationMessage()
|
|
587
591
|
});
|
|
588
592
|
return { apiKey: response.apiKey };
|
|
589
593
|
}
|
|
@@ -618,7 +622,10 @@ async function runDeviceAuthorization() {
|
|
|
618
622
|
);
|
|
619
623
|
if (!tokenResponse.error) {
|
|
620
624
|
if (!tokenResponse.api_key) {
|
|
621
|
-
throw new Error("Device flow completed but no
|
|
625
|
+
throw new Error("Device flow completed but no API key was returned");
|
|
626
|
+
}
|
|
627
|
+
if (!isProvisionedApiKey(tokenResponse.api_key)) {
|
|
628
|
+
throw new Error("Device flow returned an API key that does not start with snp-");
|
|
622
629
|
}
|
|
623
630
|
spinner.succeed(
|
|
624
631
|
tokenResponse.project_name ? `Authorized ${tokenResponse.project_name}` : "Authorized Snipara account"
|
|
@@ -669,6 +676,37 @@ function openBrowser(url) {
|
|
|
669
676
|
function sleep(ms) {
|
|
670
677
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
671
678
|
}
|
|
679
|
+
function isManualApiKey(value) {
|
|
680
|
+
const trimmed = value.trim();
|
|
681
|
+
return trimmed.startsWith("snp-") || trimmed.startsWith("rlm_");
|
|
682
|
+
}
|
|
683
|
+
function manualApiKeyValidationMessage() {
|
|
684
|
+
return "API key should start with snp- or legacy rlm_";
|
|
685
|
+
}
|
|
686
|
+
function isProvisionedApiKey(value) {
|
|
687
|
+
return value.trim().startsWith("snp-");
|
|
688
|
+
}
|
|
689
|
+
function provisionedApiKeyValidationMessage() {
|
|
690
|
+
return "New Snipara API keys should start with snp-";
|
|
691
|
+
}
|
|
692
|
+
function detectCompanionCommand() {
|
|
693
|
+
if (commandExists(COMPANION_COMMAND_NAME)) {
|
|
694
|
+
return COMPANION_COMMAND_NAME;
|
|
695
|
+
}
|
|
696
|
+
if (commandExists(LEGACY_COMPANION_COMMAND_NAME)) {
|
|
697
|
+
return LEGACY_COMPANION_COMMAND_NAME;
|
|
698
|
+
}
|
|
699
|
+
return null;
|
|
700
|
+
}
|
|
701
|
+
function detectSandboxCommand() {
|
|
702
|
+
if (commandExists(SANDBOX_COMMAND_NAME)) {
|
|
703
|
+
return SANDBOX_COMMAND_NAME;
|
|
704
|
+
}
|
|
705
|
+
if (commandExists(LEGACY_SANDBOX_COMMAND_NAME)) {
|
|
706
|
+
return LEGACY_SANDBOX_COMMAND_NAME;
|
|
707
|
+
}
|
|
708
|
+
return null;
|
|
709
|
+
}
|
|
672
710
|
async function installPythonPackages(answers, upgrade) {
|
|
673
711
|
console.log(chalk.bold(`
|
|
674
712
|
\u{1F4E6} ${upgrade ? "Upgrading" : "Installing"} Python Packages
|
|
@@ -680,7 +718,7 @@ async function installPythonPackages(answers, upgrade) {
|
|
|
680
718
|
console.log(chalk.gray(` pip install ${upgrade ? "-U " : ""}${MCP_PACKAGE_NAME}`));
|
|
681
719
|
}
|
|
682
720
|
if (answers.installRuntime) {
|
|
683
|
-
console.log(chalk.gray(` pip install ${upgrade ? "-U " : ""}${
|
|
721
|
+
console.log(chalk.gray(` pip install ${upgrade ? "-U " : ""}${SANDBOX_PACKAGE_NAME}`));
|
|
684
722
|
}
|
|
685
723
|
console.log("");
|
|
686
724
|
return;
|
|
@@ -690,27 +728,40 @@ async function installPythonPackages(answers, upgrade) {
|
|
|
690
728
|
packages.push(MCP_PACKAGE_NAME);
|
|
691
729
|
}
|
|
692
730
|
if (answers.installRuntime) {
|
|
693
|
-
packages.push(
|
|
731
|
+
packages.push(SANDBOX_PACKAGE_NAME);
|
|
694
732
|
}
|
|
695
733
|
if (packages.length === 0) return;
|
|
696
734
|
const spinner = ora(
|
|
697
735
|
`${upgrade ? "Upgrading" : "Installing"} ${packages.map(stripExtras).join(", ")}...`
|
|
698
736
|
).start();
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
737
|
+
const attempts = [packages];
|
|
738
|
+
const failures = [];
|
|
739
|
+
for (const attemptPackages of attempts) {
|
|
740
|
+
try {
|
|
741
|
+
const installCmd = pipCmd === "uv" ? `uv pip install ${upgrade ? "--upgrade " : ""}${attemptPackages.join(" ")}` : `${pipCmd} install ${upgrade ? "--upgrade " : ""}${attemptPackages.join(" ")}`;
|
|
742
|
+
execSync(installCmd, {
|
|
743
|
+
stdio: "pipe",
|
|
744
|
+
encoding: "utf-8"
|
|
745
|
+
});
|
|
746
|
+
spinner.succeed(
|
|
747
|
+
`${upgrade ? "Upgraded" : "Installed"} ${attemptPackages.map(stripExtras).join(", ")}`
|
|
748
|
+
);
|
|
749
|
+
return;
|
|
750
|
+
} catch (error) {
|
|
751
|
+
const detail = error instanceof Error && "message" in error ? error.message.split("\n")[0] : "unknown error";
|
|
752
|
+
failures.push(`${attemptPackages.join(" ")}: ${detail}`);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
spinner.fail(`Failed to ${upgrade ? "upgrade" : "install"} Python packages`);
|
|
756
|
+
if (failures.length > 0) {
|
|
757
|
+
console.log(chalk.yellow("\n Attempt summary:"));
|
|
758
|
+
for (const failure of failures) {
|
|
759
|
+
console.log(chalk.gray(` - ${failure}`));
|
|
760
|
+
}
|
|
713
761
|
}
|
|
762
|
+
console.log(chalk.yellow("\n Install manually with:"));
|
|
763
|
+
console.log(chalk.gray(` pip install ${upgrade ? "--upgrade " : ""}${packages.join(" ")}
|
|
764
|
+
`));
|
|
714
765
|
}
|
|
715
766
|
function stripExtras(name) {
|
|
716
767
|
return name.replace(/\[.*\]$/, "");
|
|
@@ -753,14 +804,15 @@ async function installNodeCompanion(upgrade) {
|
|
|
753
804
|
stdio: "pipe",
|
|
754
805
|
encoding: "utf-8"
|
|
755
806
|
});
|
|
756
|
-
|
|
807
|
+
const installedCommand = detectCompanionCommand();
|
|
808
|
+
if (installedCommand) {
|
|
757
809
|
spinner.succeed(
|
|
758
810
|
`${upgrade ? "Upgraded" : "Installed"} ${COMPANION_PACKAGE_NAME} via ${packageManager}`
|
|
759
811
|
);
|
|
760
812
|
return;
|
|
761
813
|
}
|
|
762
814
|
failures.push(
|
|
763
|
-
`${packageManager}: command completed but
|
|
815
|
+
`${packageManager}: command completed but ${COMPANION_COMMAND_NAME} is still not available on PATH (legacy ${LEGACY_COMPANION_COMMAND_NAME} is also accepted)`
|
|
764
816
|
);
|
|
765
817
|
} catch (error) {
|
|
766
818
|
const detail = error instanceof Error && "message" in error ? error.message.split("\n")[0] : "unknown error";
|
|
@@ -802,7 +854,7 @@ async function createMcpConfig(answers) {
|
|
|
802
854
|
};
|
|
803
855
|
}
|
|
804
856
|
if (answers.installRuntime) {
|
|
805
|
-
mcpConfig.mcpServers[
|
|
857
|
+
mcpConfig.mcpServers[SANDBOX_SERVER_NAME] = {
|
|
806
858
|
type: "http",
|
|
807
859
|
url: "http://localhost:8765/mcp",
|
|
808
860
|
headers: {}
|
|
@@ -887,25 +939,25 @@ function buildAgentsMdTemplate(answers) {
|
|
|
887
939
|
This project uses Snipara Hosted MCP for project context and reviewed memory.
|
|
888
940
|
|
|
889
941
|
- Endpoint: \`${buildHostedMcpEndpoint(answers)}\`
|
|
890
|
-
- Auth: use \`SNIPARA_API_KEY\`
|
|
942
|
+
- Auth: use \`SNIPARA_API_KEY\` from the environment. Never commit keys.
|
|
891
943
|
|
|
892
944
|
Agent task lifecycle:
|
|
893
945
|
|
|
894
|
-
1. Start substantial work with \`
|
|
895
|
-
2. Use \`
|
|
896
|
-
3. When \`
|
|
897
|
-
4. Use \`
|
|
898
|
-
5. Use \`
|
|
899
|
-
6. On paid Context plans, use \`
|
|
900
|
-
7. Use \`
|
|
901
|
-
8. When the LLM has produced a visible multi-phase plan and \`snipara-companion\` is installed, run \`
|
|
946
|
+
1. Start substantial work with \`snipara_recall\` and a targeted \`snipara_context_query\`.
|
|
947
|
+
2. Use \`snipara_context_query\` for docs, business context, client/project truth, architecture notes, runbooks, and narrative source material.
|
|
948
|
+
3. When \`snipara_context_query\` returns \`answer_pack\`, treat it as the first-pass response plan: use its source facts, caveats, source map, and verification checklist before drafting claims.
|
|
949
|
+
4. Use \`snipara_get_chunk\` to load cited source sections returned by reference-based queries before relying on precise wording.
|
|
950
|
+
5. Use \`snipara_code_callers\`, \`snipara_code_imports\`, \`snipara_code_neighbors\`, or \`snipara_code_shortest_path\` for structural code questions.
|
|
951
|
+
6. On paid Context plans, use \`snipara_code_symbol_card\` for agent-ready symbol context and \`snipara_code_impact\` before risky multi-file changes or PR reviews.
|
|
952
|
+
7. Use \`snipara_plan\` or \`snipara_decompose\` only for complex work and only when the hosted server exposes them.
|
|
953
|
+
8. When the LLM has produced a visible multi-phase plan and \`snipara-companion\` is installed, run \`snipara-companion workflow start --goal "<goal>" --plan-file <plan_file>\`, then use \`workflow phase-start\`, \`workflow phase-commit\`, \`workflow resume\`, and \`final-commit\` so compaction cannot lose the next phase. Legacy \`rlm-hook\` commands remain compatible.
|
|
902
954
|
9. Use local file reads, \`rg\`, git commands, and test commands for exact edits, current working-tree state, and verification.
|
|
903
|
-
10. Treat every \`
|
|
955
|
+
10. Treat every \`snipara_code_*\` response as indexed context: check its indexed commit SHA, indexing time, included file sample, coverage, freshness warnings, and sync guidance before relying on it. Legacy \`rlm_code_*\` tool IDs return the same payloads.
|
|
904
956
|
11. If \`git status --short\` is dirty, warn that Snipara's indexed code graph may not include local edits; use local reads/tests for exact edits and Snipara for architecture/code graph context.
|
|
905
|
-
12. Use
|
|
957
|
+
12. Use Snipara Sandbox only when sandboxed execution, repeatable validation, or isolated transformations materially help.
|
|
906
958
|
13. If a broad query is slow, retry once with a narrow keyword query before falling back to local search.
|
|
907
|
-
14. End each substantial phase with \`
|
|
908
|
-
15. Use \`
|
|
959
|
+
14. End each substantial phase with \`snipara_end_of_task_commit\` or \`snipara-companion workflow phase-commit\` when companion manages the plan. End the whole managed workflow with \`snipara-companion final-commit\`.
|
|
960
|
+
15. Use \`snipara_remember_if_novel\` for one reusable memory while avoiding duplicates, and use \`snipara_remember\` for explicit direct memory writes.
|
|
909
961
|
16. Do not store secrets, tokens, passwords, private keys, raw logs, transient command output, or unreviewed guesses in memory.
|
|
910
962
|
`;
|
|
911
963
|
}
|
|
@@ -919,17 +971,17 @@ Canonical project context is in \`AGENTS.md\`. This file exists for Claude Code
|
|
|
919
971
|
Use Snipara Hosted MCP before answering project-specific questions.
|
|
920
972
|
|
|
921
973
|
- Endpoint: \`${buildHostedMcpEndpoint(answers)}\`
|
|
922
|
-
- Start substantial work with \`
|
|
923
|
-
- Use \`
|
|
924
|
-
- When \`
|
|
925
|
-
- Use \`
|
|
926
|
-
- On paid Context plans, use \`
|
|
927
|
-
- Check every \`
|
|
974
|
+
- Start substantial work with \`snipara_recall\` and a targeted \`snipara_context_query\`.
|
|
975
|
+
- Use \`snipara_context_query\` for source truth and \`snipara_get_chunk\` for exact cited sections.
|
|
976
|
+
- When \`snipara_context_query\` returns \`answer_pack\`, use its facts, caveats, source map, and verification checklist before drafting claims.
|
|
977
|
+
- Use \`snipara_code_callers\`, \`snipara_code_imports\`, \`snipara_code_neighbors\`, or \`snipara_code_shortest_path\` for structural code questions.
|
|
978
|
+
- On paid Context plans, use \`snipara_code_symbol_card\` before editing important symbols and \`snipara_code_impact\` before risky multi-file changes or PR reviews.
|
|
979
|
+
- Check every \`snipara_code_*\` response for indexed commit SHA, indexing time, included files, coverage, and freshness warnings. Legacy \`rlm_code_*\` IDs return the same payloads.
|
|
928
980
|
- Use local file reads, \`rg\`, \`git status --short\`, and tests for exact edits/current working tree; use Snipara for architecture and indexed code graph context.
|
|
929
|
-
- When the plan is visible and \`snipara-companion\` is installed, use \`
|
|
930
|
-
- Use
|
|
931
|
-
- End substantial work with \`
|
|
932
|
-
- Use \`
|
|
981
|
+
- When the plan is visible and \`snipara-companion\` is installed, use \`snipara-companion workflow start\`, \`phase-start\`, \`phase-commit\`, \`workflow resume\`, and \`final-commit\` to keep phase state durable across compaction. Legacy \`rlm-hook\` commands remain compatible.
|
|
982
|
+
- Use Snipara Sandbox only when sandboxed execution or repeatable validation materially helps.
|
|
983
|
+
- End substantial work with \`snipara_end_of_task_commit\` when available; for managed workflows, end every phase with \`snipara-companion workflow phase-commit\` and the task with \`snipara-companion final-commit\`.
|
|
984
|
+
- Use \`snipara_remember_if_novel\` or \`snipara_remember\` only for narrow durable memories.
|
|
933
985
|
- Do not store secrets, one-off command output, raw logs, or unreviewed guesses in memory.
|
|
934
986
|
- Use the Snipara Claude Code plugin only when slash commands, skills, or hooks are helpful; Hosted MCP remains the normal agent path.
|
|
935
987
|
`;
|
|
@@ -946,18 +998,18 @@ This workspace uses Snipara Hosted MCP for current project context, business/cli
|
|
|
946
998
|
|
|
947
999
|
- Check the configured Snipara MCP server before answering project-specific questions.
|
|
948
1000
|
- Endpoint: \`${buildHostedMcpEndpoint(answers)}\`
|
|
949
|
-
- Use \`
|
|
950
|
-
- Use \`
|
|
951
|
-
- When \`
|
|
952
|
-
- Use \`
|
|
953
|
-
- Use \`
|
|
954
|
-
- On paid Context plans, use \`
|
|
955
|
-
- Check every \`
|
|
956
|
-
- Use \`
|
|
1001
|
+
- Use \`snipara_recall\` for durable decisions, preferences, and validated learnings.
|
|
1002
|
+
- Use \`snipara_context_query\` for docs, business context, current client/project truth, and narrative source material.
|
|
1003
|
+
- When \`snipara_context_query\` returns \`answer_pack\`, use its facts, caveats, source map, and verification checklist before drafting claims.
|
|
1004
|
+
- Use \`snipara_get_chunk\` before relying on exact cited wording from reference-based results.
|
|
1005
|
+
- Use \`snipara_code_callers\`, \`snipara_code_imports\`, \`snipara_code_neighbors\`, or \`snipara_code_shortest_path\` for structural code questions.
|
|
1006
|
+
- On paid Context plans, use \`snipara_code_symbol_card\` before editing important symbols and \`snipara_code_impact\` before risky multi-file changes or PR reviews.
|
|
1007
|
+
- Check every \`snipara_code_*\` response for indexed commit SHA, indexing time, included files, coverage, and freshness warnings. Legacy \`rlm_code_*\` IDs return the same payloads.
|
|
1008
|
+
- Use \`snipara_plan\` or \`snipara_decompose\` only for complex work and only when available.
|
|
957
1009
|
- Use local file reads, \`rg\`, \`git status --short\`, and tests for exact code edits and current working-tree state.
|
|
958
|
-
- When \`snipara-companion\` is installed and the LLM has a visible multi-phase plan, use \`
|
|
959
|
-
- Use
|
|
960
|
-
- End substantial work with \`
|
|
1010
|
+
- When \`snipara-companion\` is installed and the LLM has a visible multi-phase plan, use \`snipara-companion workflow start\`, \`phase-start\`, \`phase-commit\`, \`workflow resume\`, and \`final-commit\` so the next phase survives compaction. Legacy \`rlm-hook\` commands remain compatible.
|
|
1011
|
+
- Use Snipara Sandbox only when sandboxed execution or repeatable validation materially helps.
|
|
1012
|
+
- End substantial work with \`snipara_end_of_task_commit\` when available; for managed workflows, end every phase with \`snipara-companion workflow phase-commit\` and the task with \`snipara-companion final-commit\`. Use \`snipara_remember_if_novel\` or \`snipara_remember\` only for narrow durable memories. Never store secrets.
|
|
961
1013
|
`;
|
|
962
1014
|
}
|
|
963
1015
|
function buildCodexConfigTemplate(answers) {
|
|
@@ -1022,8 +1074,8 @@ async function createClaudeDesktopConfig(answers) {
|
|
|
1022
1074
|
}
|
|
1023
1075
|
};
|
|
1024
1076
|
if (answers.installRuntime) {
|
|
1025
|
-
config.mcpServers[
|
|
1026
|
-
command:
|
|
1077
|
+
config.mcpServers[SANDBOX_SERVER_NAME] = {
|
|
1078
|
+
command: detectSandboxCommand() || SANDBOX_COMMAND_NAME,
|
|
1027
1079
|
args: ["mcp-serve"],
|
|
1028
1080
|
env: {
|
|
1029
1081
|
SNIPARA_API_KEY: answers.apiKey,
|
|
@@ -1117,11 +1169,18 @@ set -euo pipefail
|
|
|
1117
1169
|
export SNIPARA_PROJECT_SLUG="${answers.projectSlug}"
|
|
1118
1170
|
${answers.projectId ? `export SNIPARA_PROJECT_ID="${answers.projectId}"` : ""}
|
|
1119
1171
|
|
|
1120
|
-
|
|
1172
|
+
SNIPARA_COMPANION_BIN=""
|
|
1173
|
+
if command -v ${COMPANION_COMMAND_NAME} >/dev/null 2>&1; then
|
|
1174
|
+
SNIPARA_COMPANION_BIN="${COMPANION_COMMAND_NAME}"
|
|
1175
|
+
elif command -v ${LEGACY_COMPANION_COMMAND_NAME} >/dev/null 2>&1; then
|
|
1176
|
+
SNIPARA_COMPANION_BIN="${LEGACY_COMPANION_COMMAND_NAME}"
|
|
1177
|
+
fi
|
|
1178
|
+
|
|
1179
|
+
if [ -n "$SNIPARA_COMPANION_BIN" ]; then
|
|
1121
1180
|
if [ -f ".snipara/workflow/current.json" ]; then
|
|
1122
|
-
|
|
1181
|
+
"$SNIPARA_COMPANION_BIN" workflow resume --include-session-context || true
|
|
1123
1182
|
else
|
|
1124
|
-
|
|
1183
|
+
"$SNIPARA_COMPANION_BIN" session-bootstrap || true
|
|
1125
1184
|
fi
|
|
1126
1185
|
fi
|
|
1127
1186
|
`;
|
|
@@ -1133,8 +1192,15 @@ set -euo pipefail
|
|
|
1133
1192
|
export SNIPARA_PROJECT_SLUG="${answers.projectSlug}"
|
|
1134
1193
|
${answers.projectId ? `export SNIPARA_PROJECT_ID="${answers.projectId}"` : ""}
|
|
1135
1194
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1195
|
+
SNIPARA_COMPANION_BIN=""
|
|
1196
|
+
if command -v ${COMPANION_COMMAND_NAME} >/dev/null 2>&1; then
|
|
1197
|
+
SNIPARA_COMPANION_BIN="${COMPANION_COMMAND_NAME}"
|
|
1198
|
+
elif command -v ${LEGACY_COMPANION_COMMAND_NAME} >/dev/null 2>&1; then
|
|
1199
|
+
SNIPARA_COMPANION_BIN="${LEGACY_COMPANION_COMMAND_NAME}"
|
|
1200
|
+
fi
|
|
1201
|
+
|
|
1202
|
+
if [ -n "$SNIPARA_COMPANION_BIN" ]; then
|
|
1203
|
+
"$SNIPARA_COMPANION_BIN" emit-event --event-type tool_result --json '{"source":"claude-code-hook"}' || true
|
|
1138
1204
|
fi
|
|
1139
1205
|
`;
|
|
1140
1206
|
}
|
|
@@ -1145,11 +1211,18 @@ set -euo pipefail
|
|
|
1145
1211
|
export SNIPARA_PROJECT_SLUG="${answers.projectSlug}"
|
|
1146
1212
|
${answers.projectId ? `export SNIPARA_PROJECT_ID="${answers.projectId}"` : ""}
|
|
1147
1213
|
|
|
1148
|
-
|
|
1214
|
+
SNIPARA_COMPANION_BIN=""
|
|
1215
|
+
if command -v ${COMPANION_COMMAND_NAME} >/dev/null 2>&1; then
|
|
1216
|
+
SNIPARA_COMPANION_BIN="${COMPANION_COMMAND_NAME}"
|
|
1217
|
+
elif command -v ${LEGACY_COMPANION_COMMAND_NAME} >/dev/null 2>&1; then
|
|
1218
|
+
SNIPARA_COMPANION_BIN="${LEGACY_COMPANION_COMMAND_NAME}"
|
|
1219
|
+
fi
|
|
1220
|
+
|
|
1221
|
+
if [ -n "$SNIPARA_COMPANION_BIN" ]; then
|
|
1149
1222
|
if [ -f ".snipara/workflow/current.json" ]; then
|
|
1150
|
-
|
|
1223
|
+
"$SNIPARA_COMPANION_BIN" task-commit --summary "Claude Code compacted while a managed workflow state existed. Resume with snipara-companion workflow resume --include-session-context and continue from .snipara/workflow/current.json." --category workflow-compaction --outcome partial || true
|
|
1151
1224
|
else
|
|
1152
|
-
|
|
1225
|
+
"$SNIPARA_COMPANION_BIN" task-commit --summary "Claude Code compacted local session context" --outcome partial || true
|
|
1153
1226
|
fi
|
|
1154
1227
|
fi
|
|
1155
1228
|
`;
|
|
@@ -1172,7 +1245,7 @@ ${answers.projectId ? `SNIPARA_PROJECT_ID=${answers.projectId}` : ""}
|
|
|
1172
1245
|
const llmVarName = answers.llmProvider === "openai" ? "OPENAI_API_KEY" : "ANTHROPIC_API_KEY";
|
|
1173
1246
|
envVars += `
|
|
1174
1247
|
|
|
1175
|
-
#
|
|
1248
|
+
# Snipara Sandbox LLM Provider (for snipara-sandbox run/agent CLI)
|
|
1176
1249
|
${llmVarName}=${answers.llmApiKey}`;
|
|
1177
1250
|
}
|
|
1178
1251
|
envVars = envVars.trim();
|
|
@@ -1213,7 +1286,7 @@ ${envVars}
|
|
|
1213
1286
|
function getClientGuidance(client) {
|
|
1214
1287
|
if (client === "claude-code") {
|
|
1215
1288
|
return [
|
|
1216
|
-
"Claude Code can use both the hosted MCP surface and the local
|
|
1289
|
+
"Claude Code can use both the hosted MCP surface and the local snipara-companion flows.",
|
|
1217
1290
|
"If hooks are enabled, the edge layer forwards lifecycle signals without owning durable memory locally."
|
|
1218
1291
|
];
|
|
1219
1292
|
}
|
|
@@ -1248,30 +1321,30 @@ function getClientGuidance(client) {
|
|
|
1248
1321
|
}
|
|
1249
1322
|
function getCompanionCommands(answers) {
|
|
1250
1323
|
const projectIdFlag = answers.projectId ? ` --project-id ${answers.projectId}` : "";
|
|
1251
|
-
const apiKeyFlag = answers.apiKeyType === "none" || answers.apiKey.includes("YOUR_") ? " --api-key <
|
|
1324
|
+
const apiKeyFlag = answers.apiKeyType === "none" || answers.apiKey.includes("YOUR_") ? " --api-key <rlm-key>" : ` --api-key ${answers.apiKey}`;
|
|
1252
1325
|
const clientFlag = getCompanionInitClientFlag(answers.aiClient);
|
|
1253
1326
|
const businessOnboardingCommands = answers.installCompanion ? [
|
|
1254
1327
|
{
|
|
1255
1328
|
title: "Preview a business folder import",
|
|
1256
|
-
command:
|
|
1329
|
+
command: `${COMPANION_COMMAND_NAME} onboard-folder ./client-export --source-provider local_folder --write-manifest ./snipara-onboard.json`,
|
|
1257
1330
|
purpose: "Classify a local or LLM-materialized business folder and review the Snipara sync manifest before upload."
|
|
1258
1331
|
},
|
|
1259
1332
|
{
|
|
1260
1333
|
title: "Apply a business folder import",
|
|
1261
|
-
command:
|
|
1334
|
+
command: `${COMPANION_COMMAND_NAME} onboard-folder ./client-export --source-provider local_folder --apply`,
|
|
1262
1335
|
purpose: "Upload approved business-context documents to hosted Snipara and trigger document reindexing."
|
|
1263
1336
|
}
|
|
1264
1337
|
] : [
|
|
1265
1338
|
{
|
|
1266
1339
|
title: "Enable business folder onboarding",
|
|
1267
1340
|
command: "npx create-snipara repair --with-companion",
|
|
1268
|
-
purpose:
|
|
1341
|
+
purpose: `Install ${COMPANION_PACKAGE_NAME} so this project can run ${COMPANION_COMMAND_NAME} onboard-folder without using the dashboard.`
|
|
1269
1342
|
}
|
|
1270
1343
|
];
|
|
1271
1344
|
return [
|
|
1272
1345
|
{
|
|
1273
1346
|
title: "Bootstrap local companion",
|
|
1274
|
-
command:
|
|
1347
|
+
command: `${COMPANION_COMMAND_NAME} init${clientFlag}${projectIdFlag}${apiKeyFlag}`,
|
|
1275
1348
|
purpose: "Write local companion config for this workstation."
|
|
1276
1349
|
},
|
|
1277
1350
|
{
|
|
@@ -1286,68 +1359,68 @@ function getCompanionCommands(answers) {
|
|
|
1286
1359
|
},
|
|
1287
1360
|
{
|
|
1288
1361
|
title: "Run a focused hosted query",
|
|
1289
|
-
command:
|
|
1362
|
+
command: `${COMPANION_COMMAND_NAME} query --query "how does auth middleware work?" --max-tokens 4000`,
|
|
1290
1363
|
purpose: "Pull ranked source context before editing, reviewing, or drafting."
|
|
1291
1364
|
},
|
|
1292
1365
|
{
|
|
1293
1366
|
title: "Load linked standards only when needed",
|
|
1294
|
-
command:
|
|
1367
|
+
command: `${COMPANION_COMMAND_NAME} shared-context --categories MANDATORY BEST_PRACTICES`,
|
|
1295
1368
|
purpose: "Fetch project-linked team or workspace guidance without treating it as memory."
|
|
1296
1369
|
},
|
|
1297
1370
|
{
|
|
1298
1371
|
title: "Generate a hosted plan",
|
|
1299
|
-
command:
|
|
1372
|
+
command: `${COMPANION_COMMAND_NAME} plan --query "Implement webhook retry hardening" --max-tokens 3000`,
|
|
1300
1373
|
purpose: "Produce a structured execution plan against indexed project knowledge."
|
|
1301
1374
|
},
|
|
1302
1375
|
{
|
|
1303
1376
|
title: "Start a managed workflow from the visible plan",
|
|
1304
|
-
command:
|
|
1377
|
+
command: `${COMPANION_COMMAND_NAME} workflow start --goal "Implement webhook retry hardening" --plan-file ./plan.md`,
|
|
1305
1378
|
purpose: "Record the LLM-visible plan in .snipara/workflow/current.json so phase state survives compaction."
|
|
1306
1379
|
},
|
|
1307
1380
|
{
|
|
1308
1381
|
title: "Start the current workflow phase",
|
|
1309
|
-
command:
|
|
1382
|
+
command: `${COMPANION_COMMAND_NAME} workflow phase-start context`,
|
|
1310
1383
|
purpose: "Mark a phase active and print the required Snipara context gate for the agent."
|
|
1311
1384
|
},
|
|
1312
1385
|
{
|
|
1313
1386
|
title: "Analyze code impact before a risky change",
|
|
1314
|
-
command:
|
|
1387
|
+
command: `${COMPANION_COMMAND_NAME} code impact --changed-files src/api/webhooks.ts tests/webhooks.test.ts --diff-summary "webhook retry hardening"`,
|
|
1315
1388
|
purpose: "Use paid Context code impact analysis to surface likely tests, docs, routes, config, and risks."
|
|
1316
1389
|
},
|
|
1317
1390
|
{
|
|
1318
1391
|
title: "Upload or refresh a doc",
|
|
1319
|
-
command:
|
|
1392
|
+
command: `${COMPANION_COMMAND_NAME} upload --path docs/local-playbook.md --file ./docs/local-playbook.md`,
|
|
1320
1393
|
purpose: "Push local notes or docs into hosted Snipara context."
|
|
1321
1394
|
},
|
|
1322
1395
|
...businessOnboardingCommands,
|
|
1323
1396
|
{
|
|
1324
1397
|
title: "Inspect a specific chunk",
|
|
1325
|
-
command:
|
|
1398
|
+
command: `${COMPANION_COMMAND_NAME} chunk get --chunk-id <chunk_id>`,
|
|
1326
1399
|
purpose: "Open an exact chunk returned by references-based search."
|
|
1327
1400
|
},
|
|
1328
1401
|
{
|
|
1329
1402
|
title: "Bootstrap the session memory view",
|
|
1330
|
-
command:
|
|
1403
|
+
command: `${COMPANION_COMMAND_NAME} session-bootstrap`,
|
|
1331
1404
|
purpose: "Load durable decisions, learnings, and optional short-lived carryover at session start."
|
|
1332
1405
|
},
|
|
1333
1406
|
{
|
|
1334
1407
|
title: "Ask memory about past decisions",
|
|
1335
|
-
command:
|
|
1408
|
+
command: `${COMPANION_COMMAND_NAME} recall --query "What did we decide about auth retries?" --type decision`,
|
|
1336
1409
|
purpose: "Query durable memory for past decisions, preferences, or validated learnings."
|
|
1337
1410
|
},
|
|
1338
1411
|
{
|
|
1339
1412
|
title: "Commit durable task memory",
|
|
1340
|
-
command:
|
|
1413
|
+
command: `${COMPANION_COMMAND_NAME} task-commit --summary "Hardened webhook retries and updated tests" --category engineering --outcome completed --files src/api/webhooks.ts tests/webhooks.test.ts`,
|
|
1341
1414
|
purpose: "Persist reusable outcomes back into hosted memory after the work is done."
|
|
1342
1415
|
},
|
|
1343
1416
|
{
|
|
1344
1417
|
title: "Commit a managed workflow phase",
|
|
1345
|
-
command:
|
|
1418
|
+
command: `${COMPANION_COMMAND_NAME} workflow phase-commit context --summary "Loaded context and mapped impacted files" --files src/api/webhooks.ts tests/webhooks.test.ts`,
|
|
1346
1419
|
purpose: "Persist this phase through hosted memory and advance the local managed workflow state."
|
|
1347
1420
|
},
|
|
1348
1421
|
{
|
|
1349
1422
|
title: "Commit the final managed workflow result",
|
|
1350
|
-
command:
|
|
1423
|
+
command: `${COMPANION_COMMAND_NAME} final-commit --summary "Shipped webhook retry hardening and tests" --files src/api/webhooks.ts tests/webhooks.test.ts`,
|
|
1351
1424
|
purpose: "Persist the final workflow outcome and close the managed workflow after verification."
|
|
1352
1425
|
}
|
|
1353
1426
|
];
|
|
@@ -1372,7 +1445,7 @@ This project uses Snipara as a hosted-first context and memory layer with local
|
|
|
1372
1445
|
- Installed local companion: \`${answers.installCompanion ? "yes" : "no"}\`
|
|
1373
1446
|
- Generated hosted MCP config: \`${answers.profile !== "runtime-only" ? "yes" : "no"}\`
|
|
1374
1447
|
- Installed local snipara-mcp package: \`${answers.installMcp ? "yes" : "no"}\`
|
|
1375
|
-
- Installed
|
|
1448
|
+
- Installed Snipara Sandbox: \`${answers.installRuntime ? "yes" : "no"}\`
|
|
1376
1449
|
- Generated hooks: \`${answers.installHooks ? "yes" : "no"}\`
|
|
1377
1450
|
|
|
1378
1451
|
## Local Detection Snapshot
|
|
@@ -1381,7 +1454,7 @@ This project uses Snipara as a hosted-first context and memory layer with local
|
|
|
1381
1454
|
- .mcp.json present: \`${detection.files.mcpConfig ? "yes" : "no"}\`
|
|
1382
1455
|
- .env.local present: \`${detection.files.envLocal ? "yes" : "no"}\`
|
|
1383
1456
|
- Companion CLI on PATH: \`${detection.tooling.companionCli ? "yes" : "no"}\`
|
|
1384
|
-
-
|
|
1457
|
+
- Snipara Sandbox CLI on PATH: \`${detection.tooling.runtimeCli ? "yes" : "no"}\`
|
|
1385
1458
|
|
|
1386
1459
|
## Client Guidance
|
|
1387
1460
|
|
|
@@ -1393,11 +1466,11 @@ Use this when Claude, ChatGPT, Codex, or another LLM client has already material
|
|
|
1393
1466
|
|
|
1394
1467
|
${answers.installCompanion ? `Preview first:
|
|
1395
1468
|
|
|
1396
|
-
|
|
1469
|
+
\`${COMPANION_COMMAND_NAME} onboard-folder ./client-export --source-provider local_folder --write-manifest ./snipara-onboard.json\`
|
|
1397
1470
|
|
|
1398
1471
|
Apply only after reviewing the manifest:
|
|
1399
1472
|
|
|
1400
|
-
|
|
1473
|
+
\`${COMPANION_COMMAND_NAME} onboard-folder ./client-export --source-provider local_folder --apply\`` : `This setup did not install snipara-companion. Enable it before business folder imports:
|
|
1401
1474
|
|
|
1402
1475
|
\`npx create-snipara repair --with-companion\``}
|
|
1403
1476
|
|
|
@@ -1449,30 +1522,36 @@ ${item.purpose}`
|
|
|
1449
1522
|
console.log(chalk.green(" \u2713 Created .snipara/companion/README.md"));
|
|
1450
1523
|
console.log(chalk.green(" \u2713 Created .snipara/companion/commands.json"));
|
|
1451
1524
|
}
|
|
1452
|
-
async function
|
|
1453
|
-
|
|
1525
|
+
async function runSandboxInit() {
|
|
1526
|
+
const sandboxCommand = detectSandboxCommand() || SANDBOX_COMMAND_NAME;
|
|
1527
|
+
console.log(chalk.bold("\n\u{1F527} Running Snipara Sandbox Configuration\n"));
|
|
1454
1528
|
console.log(
|
|
1455
|
-
chalk.dim(
|
|
1529
|
+
chalk.dim(
|
|
1530
|
+
` Starting '${sandboxCommand} init' - follow the prompts to configure execution environment...
|
|
1531
|
+
`
|
|
1532
|
+
)
|
|
1456
1533
|
);
|
|
1457
1534
|
return new Promise((resolve) => {
|
|
1458
|
-
const child = spawn(
|
|
1535
|
+
const child = spawn(sandboxCommand, ["init"], {
|
|
1459
1536
|
stdio: "inherit",
|
|
1460
1537
|
shell: true
|
|
1461
1538
|
});
|
|
1462
1539
|
child.on("close", (code) => {
|
|
1463
1540
|
if (code === 0) {
|
|
1464
|
-
console.log(chalk.green("\n \u2713
|
|
1541
|
+
console.log(chalk.green("\n \u2713 Snipara Sandbox configured successfully"));
|
|
1465
1542
|
} else if (code !== null) {
|
|
1466
1543
|
console.log(chalk.yellow(`
|
|
1467
|
-
\u26A0\uFE0F
|
|
1468
|
-
console.log(chalk.dim(
|
|
1544
|
+
\u26A0\uFE0F ${sandboxCommand} init exited with code ${code}`));
|
|
1545
|
+
console.log(chalk.dim(` You can run '${sandboxCommand} init' manually later.`));
|
|
1469
1546
|
}
|
|
1470
1547
|
resolve();
|
|
1471
1548
|
});
|
|
1472
1549
|
child.on("error", (error) => {
|
|
1473
1550
|
console.log(chalk.yellow(`
|
|
1474
|
-
\u26A0\uFE0F Could not run
|
|
1475
|
-
console.log(
|
|
1551
|
+
\u26A0\uFE0F Could not run ${sandboxCommand} init: ${error.message}`));
|
|
1552
|
+
console.log(
|
|
1553
|
+
chalk.dim(` Run '${SANDBOX_COMMAND_NAME} init' manually after installation completes.`)
|
|
1554
|
+
);
|
|
1476
1555
|
resolve();
|
|
1477
1556
|
});
|
|
1478
1557
|
});
|
|
@@ -1527,9 +1606,9 @@ async function runValidation(answers, includeHostedCheck) {
|
|
|
1527
1606
|
detail: answers.installCompanion && !detection.tooling.companionCli ? `${COMPANION_PACKAGE_NAME} not found on PATH` : "Companion CLI ready or not requested"
|
|
1528
1607
|
});
|
|
1529
1608
|
checks.push({
|
|
1530
|
-
name: "
|
|
1609
|
+
name: "sandbox_cli",
|
|
1531
1610
|
status: answers.installRuntime ? detection.tooling.runtimeCli ? "pass" : "warn" : "pass",
|
|
1532
|
-
detail: answers.installRuntime && !detection.tooling.runtimeCli ?
|
|
1611
|
+
detail: answers.installRuntime && !detection.tooling.runtimeCli ? `${SANDBOX_COMMAND_NAME} CLI not found on PATH` : "Snipara Sandbox CLI ready or not requested"
|
|
1533
1612
|
});
|
|
1534
1613
|
checks.push({
|
|
1535
1614
|
name: "companion_pack",
|
|
@@ -1625,14 +1704,14 @@ async function testHostedConnection(answers) {
|
|
|
1625
1704
|
}
|
|
1626
1705
|
];
|
|
1627
1706
|
const settingsResponse = await callHostedMcp(answers, "tools/call", {
|
|
1628
|
-
name: "
|
|
1707
|
+
name: "snipara_settings",
|
|
1629
1708
|
arguments: {}
|
|
1630
1709
|
});
|
|
1631
1710
|
if (settingsResponse.ok && !settingsResponse.data.error) {
|
|
1632
1711
|
checks.push({
|
|
1633
1712
|
name: "hosted_settings",
|
|
1634
1713
|
status: "pass",
|
|
1635
|
-
detail: "
|
|
1714
|
+
detail: "snipara_settings tool call succeeded"
|
|
1636
1715
|
});
|
|
1637
1716
|
} else {
|
|
1638
1717
|
checks.push({
|
|
@@ -1684,7 +1763,7 @@ async function callHostedMcp(answers, method, params) {
|
|
|
1684
1763
|
function describeHostedHttpFailure(status, bodyText) {
|
|
1685
1764
|
const detail = bodyText.trim().slice(0, 120);
|
|
1686
1765
|
if (status === 401 || status === 403) {
|
|
1687
|
-
return `Auth failed (${status}). Check the
|
|
1766
|
+
return `Auth failed (${status}). Check the API key and project slug.`;
|
|
1688
1767
|
}
|
|
1689
1768
|
if (status === 404) {
|
|
1690
1769
|
return "Endpoint or project slug was not found. Check the hosted MCP URL.";
|
|
@@ -1723,7 +1802,9 @@ function printSuccess(answers, report) {
|
|
|
1723
1802
|
);
|
|
1724
1803
|
}
|
|
1725
1804
|
if (answers.installRuntime) {
|
|
1726
|
-
console.log(
|
|
1805
|
+
console.log(
|
|
1806
|
+
chalk.green(` \u2713 ${stripExtras(SANDBOX_PACKAGE_NAME)}`) + chalk.dim(" - Safe code execution")
|
|
1807
|
+
);
|
|
1727
1808
|
}
|
|
1728
1809
|
if (answers.installCompanion) {
|
|
1729
1810
|
console.log(
|
|
@@ -1754,20 +1835,27 @@ function printSuccess(answers, report) {
|
|
|
1754
1835
|
printNextStep("Open .snipara/companion/README.md for local commands and repair flow.");
|
|
1755
1836
|
printNextStep("Run create-snipara doctor anytime you want a fresh local validation report.");
|
|
1756
1837
|
if (answers.installCompanion) {
|
|
1757
|
-
printNextStep("Use rlm-hook query / plan / upload / task-commit for explicit local workflows.");
|
|
1758
1838
|
printNextStep(
|
|
1759
|
-
|
|
1839
|
+
`${COMPANION_COMMAND_NAME} is the primary companion command; legacy ${LEGACY_COMPANION_COMMAND_NAME} remains compatible.`
|
|
1760
1840
|
);
|
|
1761
1841
|
printNextStep(
|
|
1762
|
-
|
|
1842
|
+
`Use ${COMPANION_COMMAND_NAME} query / plan / upload / task-commit for explicit local workflows.`
|
|
1843
|
+
);
|
|
1844
|
+
printNextStep(
|
|
1845
|
+
`For business folders, preview with: ${COMPANION_COMMAND_NAME} onboard-folder ./client-export --source-provider local_folder --write-manifest ./snipara-onboard.json`
|
|
1846
|
+
);
|
|
1847
|
+
printNextStep(
|
|
1848
|
+
`Apply after review with: ${COMPANION_COMMAND_NAME} onboard-folder ./client-export --source-provider local_folder --apply`
|
|
1763
1849
|
);
|
|
1764
1850
|
} else if (answers.profile !== "runtime-only") {
|
|
1765
1851
|
printNextStep(
|
|
1766
1852
|
"For dashboardless business folder imports, enable companion with: npx create-snipara repair --with-companion"
|
|
1767
1853
|
);
|
|
1768
1854
|
}
|
|
1769
|
-
if (answers.installRuntime && !answers.
|
|
1770
|
-
printNextStep(
|
|
1855
|
+
if (answers.installRuntime && !answers.runSandboxInit) {
|
|
1856
|
+
printNextStep(
|
|
1857
|
+
`Run ${SANDBOX_COMMAND_NAME} init when you want to choose sandbox/docker/local execution.`
|
|
1858
|
+
);
|
|
1771
1859
|
}
|
|
1772
1860
|
if (answers.githubRepo) {
|
|
1773
1861
|
printNextStep(
|
|
@@ -1793,11 +1881,11 @@ function printSuccess(answers, report) {
|
|
|
1793
1881
|
}
|
|
1794
1882
|
function commandExists(command) {
|
|
1795
1883
|
try {
|
|
1796
|
-
execSync(`${command} --version`, { stdio: "ignore", shell: "/bin/sh" });
|
|
1884
|
+
execSync(`${command} --version`, { stdio: "ignore", shell: "/bin/sh", timeout: 2500 });
|
|
1797
1885
|
return true;
|
|
1798
1886
|
} catch {
|
|
1799
1887
|
try {
|
|
1800
|
-
execSync(`command -v ${command}`, { stdio: "ignore", shell: "/bin/sh" });
|
|
1888
|
+
execSync(`command -v ${command}`, { stdio: "ignore", shell: "/bin/sh", timeout: 1e3 });
|
|
1801
1889
|
return true;
|
|
1802
1890
|
} catch {
|
|
1803
1891
|
return false;
|
|
@@ -1834,8 +1922,8 @@ function detectEnvironment() {
|
|
|
1834
1922
|
uv: commandExists("uv"),
|
|
1835
1923
|
docker: commandExists("docker"),
|
|
1836
1924
|
git: commandExists("git"),
|
|
1837
|
-
companionCli:
|
|
1838
|
-
runtimeCli:
|
|
1925
|
+
companionCli: Boolean(detectCompanionCommand()),
|
|
1926
|
+
runtimeCli: Boolean(detectSandboxCommand())
|
|
1839
1927
|
},
|
|
1840
1928
|
existingConfig: {
|
|
1841
1929
|
slug: existingConfig?.slug,
|
|
@@ -1848,8 +1936,13 @@ function detectEnvironment() {
|
|
|
1848
1936
|
function loadExistingAnswers(options) {
|
|
1849
1937
|
const detection = detectEnvironment();
|
|
1850
1938
|
const effectiveProfile = resolveMaintenanceProfile(detection, options.profile);
|
|
1851
|
-
const
|
|
1852
|
-
|
|
1939
|
+
const providedApiKey = options.apiKey?.trim();
|
|
1940
|
+
if (providedApiKey && !isManualApiKey(providedApiKey)) {
|
|
1941
|
+
throw new Error(manualApiKeyValidationMessage());
|
|
1942
|
+
}
|
|
1943
|
+
const envApiKey = readApiKeyFromEnvFiles();
|
|
1944
|
+
const apiKey = providedApiKey || envApiKey || "YOUR_API_KEY_HERE";
|
|
1945
|
+
const apiKeyType = effectiveProfile === "runtime-only" ? "none" : providedApiKey || envApiKey ? "api" : "none";
|
|
1853
1946
|
return buildAnswersFromProfile({
|
|
1854
1947
|
projectSlug: options.slug || detection.existingConfig.slug || detection.projectSlug || "my-project",
|
|
1855
1948
|
projectId: options.projectId || detection.existingConfig.projectId || "",
|
|
@@ -1996,7 +2089,7 @@ function getExistingConfig() {
|
|
|
1996
2089
|
try {
|
|
1997
2090
|
const config = JSON.parse(fs.readFileSync(mcpConfigPath, "utf-8"));
|
|
1998
2091
|
const sniparaServer = config.mcpServers?.snipara;
|
|
1999
|
-
const runtimeServer = config.mcpServers?.[
|
|
2092
|
+
const runtimeServer = config.mcpServers?.[SANDBOX_SERVER_NAME] || config.mcpServers?.[LEGACY_RUNTIME_SERVER_NAME];
|
|
2000
2093
|
const match = sniparaServer?.url?.match(/\/mcp\/([^/]+)$/);
|
|
2001
2094
|
return {
|
|
2002
2095
|
slug: match?.[1],
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-snipara",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"description": "Onboard Snipara Hosted MCP Context + Memory with auth, companion workflows, and optional
|
|
3
|
+
"version": "1.3.13",
|
|
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",
|
|
7
7
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"snipara",
|
|
22
22
|
"create-snipara",
|
|
23
23
|
"mcp",
|
|
24
|
-
"
|
|
24
|
+
"snipara-sandbox",
|
|
25
25
|
"context-optimization",
|
|
26
26
|
"llm",
|
|
27
27
|
"ai-agents",
|