create-snipara 1.3.9 → 1.3.12
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 +67 -41
- package/dist/index.js +293 -157
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -48,10 +48,10 @@ The product surfaces remain:
|
|
|
48
48
|
- Hosted MCP for LLM agents, context, and reviewed memory
|
|
49
49
|
- HTTP API / SDK for apps, integrators, backends, and pipelines
|
|
50
50
|
|
|
51
|
-
The CLI
|
|
51
|
+
The default CLI path wires the local companion around those surfaces:
|
|
52
52
|
|
|
53
|
-
- `snipara-companion` for local `rlm-hook`
|
|
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,14 +59,14 @@ The CLI can wire local helpers 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-
|
|
65
|
-
| `hosted-
|
|
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
|
-
Default profile: `hosted-
|
|
69
|
+
Default profile: `hosted-companion`
|
|
70
70
|
|
|
71
71
|
## Free Account Fit
|
|
72
72
|
|
|
@@ -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
|
|
|
@@ -99,10 +99,10 @@ That distinction matters:
|
|
|
99
99
|
- opens the browser device-flow free Context + Memory account flow when needed
|
|
100
100
|
- writes Hosted MCP configuration
|
|
101
101
|
- detects a GitHub remote and can open the Snipara GitHub App automation path
|
|
102
|
-
- installs `snipara-companion`
|
|
103
|
-
- installs optional `
|
|
102
|
+
- installs `snipara-companion` by default for local managed workflows
|
|
103
|
+
- installs optional `snipara-sandbox`
|
|
104
104
|
- configures `.mcp.json`
|
|
105
|
-
- generates merge-ready AI agent templates in `.snipara/templates/`, including Pro+ symbol-card
|
|
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
|
|
107
107
|
- updates `.env`, `.env.local`, and `.env.example`
|
|
108
108
|
- generates `.snipara/companion/README.md`
|
|
@@ -131,12 +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
|
-
By default, this
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
By default, this uses the recommended `hosted-companion` setup: hosted MCP plus
|
|
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.
|
|
140
141
|
|
|
141
142
|
The advanced flow asks for:
|
|
142
143
|
|
|
@@ -144,7 +145,7 @@ The advanced flow asks for:
|
|
|
144
145
|
- optional project id
|
|
145
146
|
- whether hooks should be generated
|
|
146
147
|
- whether to open GitHub repository automation when a GitHub remote is detected
|
|
147
|
-
- optional
|
|
148
|
+
- optional sandbox provider config when `snipara-sandbox` is installed
|
|
148
149
|
|
|
149
150
|
The generated agent templates cover:
|
|
150
151
|
|
|
@@ -154,6 +155,17 @@ The generated agent templates cover:
|
|
|
154
155
|
- Codex `.codex/config.toml` snippet
|
|
155
156
|
- ChatGPT/OpenAI HTTP MCP snippet
|
|
156
157
|
|
|
158
|
+
When `snipara-companion` is installed, the companion pack also includes the compaction-safe managed workflow:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
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
|
+
```
|
|
168
|
+
|
|
157
169
|
## Main Commands
|
|
158
170
|
|
|
159
171
|
### Install or initialize
|
|
@@ -175,7 +187,7 @@ Writes `.snipara/companion/doctor.json` and validates:
|
|
|
175
187
|
|
|
176
188
|
- local config files
|
|
177
189
|
- companion CLI presence
|
|
178
|
-
-
|
|
190
|
+
- Snipara Sandbox CLI presence
|
|
179
191
|
- environment files
|
|
180
192
|
- hosted MCP reachability when a live API key is available
|
|
181
193
|
|
|
@@ -247,15 +259,15 @@ That keeps business truth review separate from agent onboarding.
|
|
|
247
259
|
|
|
248
260
|
```bash
|
|
249
261
|
# Use an explicit install profile
|
|
250
|
-
npx create-snipara --profile hosted-only
|
|
251
262
|
npx create-snipara --profile hosted-companion
|
|
263
|
+
npx create-snipara --profile hosted-only
|
|
252
264
|
npx create-snipara --profile full-stack
|
|
253
265
|
npx create-snipara --advanced
|
|
254
266
|
|
|
255
|
-
#
|
|
267
|
+
# Sandbox-only local execution
|
|
256
268
|
npx create-snipara --runtime-only
|
|
257
269
|
|
|
258
|
-
# Non-interactive setup
|
|
270
|
+
# Non-interactive setup with an existing API key
|
|
259
271
|
npx create-snipara -y --api-key rlm_xxx --slug my-project
|
|
260
272
|
|
|
261
273
|
# Open GitHub automation after setup when a GitHub remote is detected
|
|
@@ -271,6 +283,7 @@ npx create-snipara --skip-github
|
|
|
271
283
|
|
|
272
284
|
# Force extra layers during maintenance
|
|
273
285
|
npx create-snipara repair --with-hooks
|
|
286
|
+
npx create-snipara repair --with-runtime
|
|
274
287
|
npx create-snipara upgrade --with-runtime
|
|
275
288
|
```
|
|
276
289
|
|
|
@@ -279,18 +292,19 @@ npx create-snipara upgrade --with-runtime
|
|
|
279
292
|
The intended split is:
|
|
280
293
|
|
|
281
294
|
- `create-snipara`: Context + Memory onboarding, auth, config, inspect, repair, and upgrade
|
|
282
|
-
- `snipara-companion`:
|
|
295
|
+
- `snipara-companion`: default local workflow helper; legacy `rlm-hook` commands remain compatible
|
|
283
296
|
- `snipara-mcp`: local stdio bridge, client-package development, compatibility, and testing
|
|
284
|
-
- `
|
|
297
|
+
- `snipara-sandbox`: optional local execution engine
|
|
285
298
|
|
|
286
299
|
## Codex Setup
|
|
287
300
|
|
|
288
|
-
For Codex, keep the
|
|
301
|
+
For Codex, keep Hosted MCP as the core path and let companion add workflow
|
|
302
|
+
ergonomics:
|
|
289
303
|
|
|
290
304
|
- use Hosted MCP as the core Snipara surface
|
|
291
305
|
- use `AGENTS.md` for project instructions
|
|
292
|
-
-
|
|
293
|
-
-
|
|
306
|
+
- use the default `hosted-companion` path when you want compaction-safe local phase commits
|
|
307
|
+
- choose `hosted-only` or `--skip-companion` only in locked-down environments
|
|
294
308
|
- use local `snipara-mcp` only for stdio compatibility, development, or tests
|
|
295
309
|
|
|
296
310
|
Codex does not require hook presets to use Snipara successfully.
|
|
@@ -310,8 +324,8 @@ This pack is meant to be the local source of truth for machine-level setup. When
|
|
|
310
324
|
such as:
|
|
311
325
|
|
|
312
326
|
```bash
|
|
313
|
-
|
|
314
|
-
|
|
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
|
|
315
329
|
```
|
|
316
330
|
|
|
317
331
|
Use this for dashboardless business-context imports after an LLM client has
|
|
@@ -342,23 +356,35 @@ Recommended explanation to users:
|
|
|
342
356
|
|
|
343
357
|
## API Key Requirements
|
|
344
358
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
|
350
|
-
|
|
|
351
|
-
|
|
|
359
|
+
Existing manual API keys passed to `create-snipara --api-key` should start with `rlm_`.
|
|
360
|
+
When `create-snipara` provisions a new account through the browser flow, Snipara returns a
|
|
361
|
+
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 |
|
|
352
370
|
|
|
353
371
|
## Example Flows
|
|
354
372
|
|
|
355
|
-
### Default hosted setup
|
|
373
|
+
### Default managed hosted setup
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
npx create-snipara
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Best when the user wants Hosted MCP Context + Memory plus `snipara-companion` managed workflows.
|
|
380
|
+
|
|
381
|
+
### Minimal hosted setup
|
|
356
382
|
|
|
357
383
|
```bash
|
|
358
384
|
npx create-snipara --profile hosted-only
|
|
359
385
|
```
|
|
360
386
|
|
|
361
|
-
Best when the user only wants Hosted MCP Context + Memory.
|
|
387
|
+
Best when the user only wants Hosted MCP Context + Memory and cannot install local helper tooling.
|
|
362
388
|
|
|
363
389
|
### Full local stack
|
|
364
390
|
|
|
@@ -366,7 +392,7 @@ Best when the user only wants Hosted MCP Context + Memory.
|
|
|
366
392
|
npx create-snipara --profile full-stack --advanced
|
|
367
393
|
```
|
|
368
394
|
|
|
369
|
-
Best when the user also wants `snipara-
|
|
395
|
+
Best when the user also wants sandboxed `snipara-sandbox` execution.
|
|
370
396
|
|
|
371
397
|
### Connect the current repository
|
|
372
398
|
|
|
@@ -411,7 +437,7 @@ npx create-snipara print-config --json
|
|
|
411
437
|
When testing a locally packed tarball, use `npm exec --package` instead of `npx`, for example:
|
|
412
438
|
|
|
413
439
|
```bash
|
|
414
|
-
npm exec --package ./create-snipara-1.3.
|
|
440
|
+
npm exec --package ./create-snipara-1.3.12.tgz create-snipara -- --help
|
|
415
441
|
```
|
|
416
442
|
|
|
417
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.12";
|
|
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
|
};
|
|
@@ -26,11 +32,11 @@ function getCompanionInitClientFlag(aiClient) {
|
|
|
26
32
|
}
|
|
27
33
|
function buildProgram() {
|
|
28
34
|
const program = new Command();
|
|
29
|
-
program.name("create-snipara").description("Scaffold hosted Snipara MCP config plus
|
|
30
|
-
program.command("init").description("Install and configure Snipara for this project").option("--api-key <key>", "Snipara
|
|
35
|
+
program.name("create-snipara").description("Scaffold hosted Snipara MCP config plus companion workflow helpers").version(VERSION);
|
|
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 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
|
}
|
|
@@ -483,32 +487,32 @@ function resolveRequestedProfile(options) {
|
|
|
483
487
|
if (options.profile && isInstallProfile(options.profile)) {
|
|
484
488
|
return options.profile;
|
|
485
489
|
}
|
|
486
|
-
return "hosted-
|
|
490
|
+
return "hosted-companion";
|
|
487
491
|
}
|
|
488
492
|
function isInstallProfile(value) {
|
|
489
493
|
return ["hosted-only", "hosted-companion", "full-stack", "runtime-only"].includes(value);
|
|
490
494
|
}
|
|
491
495
|
function installProfileChoices() {
|
|
492
496
|
return [
|
|
493
|
-
{
|
|
494
|
-
title: "Hosted only",
|
|
495
|
-
value: "hosted-only",
|
|
496
|
-
description: "Default: hosted MCP config, no local companion or runtime"
|
|
497
|
-
},
|
|
498
497
|
{
|
|
499
498
|
title: "Hosted + Companion",
|
|
500
499
|
value: "hosted-companion",
|
|
501
|
-
description: "
|
|
500
|
+
description: "Default: hosted MCP config plus managed local workflow commands"
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
title: "Hosted only",
|
|
504
|
+
value: "hosted-only",
|
|
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 local
|
|
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,36 @@ function openBrowser(url) {
|
|
|
669
676
|
function sleep(ms) {
|
|
670
677
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
671
678
|
}
|
|
679
|
+
function isManualApiKey(value) {
|
|
680
|
+
return value.trim().startsWith("rlm_");
|
|
681
|
+
}
|
|
682
|
+
function manualApiKeyValidationMessage() {
|
|
683
|
+
return "API key should start with rlm_";
|
|
684
|
+
}
|
|
685
|
+
function isProvisionedApiKey(value) {
|
|
686
|
+
return value.trim().startsWith("snp-");
|
|
687
|
+
}
|
|
688
|
+
function provisionedApiKeyValidationMessage() {
|
|
689
|
+
return "New Snipara API keys should start with snp-";
|
|
690
|
+
}
|
|
691
|
+
function detectCompanionCommand() {
|
|
692
|
+
if (commandExists(COMPANION_COMMAND_NAME)) {
|
|
693
|
+
return COMPANION_COMMAND_NAME;
|
|
694
|
+
}
|
|
695
|
+
if (commandExists(LEGACY_COMPANION_COMMAND_NAME)) {
|
|
696
|
+
return LEGACY_COMPANION_COMMAND_NAME;
|
|
697
|
+
}
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
700
|
+
function detectSandboxCommand() {
|
|
701
|
+
if (commandExists(SANDBOX_COMMAND_NAME)) {
|
|
702
|
+
return SANDBOX_COMMAND_NAME;
|
|
703
|
+
}
|
|
704
|
+
if (commandExists(LEGACY_SANDBOX_COMMAND_NAME)) {
|
|
705
|
+
return LEGACY_SANDBOX_COMMAND_NAME;
|
|
706
|
+
}
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
672
709
|
async function installPythonPackages(answers, upgrade) {
|
|
673
710
|
console.log(chalk.bold(`
|
|
674
711
|
\u{1F4E6} ${upgrade ? "Upgrading" : "Installing"} Python Packages
|
|
@@ -680,7 +717,7 @@ async function installPythonPackages(answers, upgrade) {
|
|
|
680
717
|
console.log(chalk.gray(` pip install ${upgrade ? "-U " : ""}${MCP_PACKAGE_NAME}`));
|
|
681
718
|
}
|
|
682
719
|
if (answers.installRuntime) {
|
|
683
|
-
console.log(chalk.gray(` pip install ${upgrade ? "-U " : ""}${
|
|
720
|
+
console.log(chalk.gray(` pip install ${upgrade ? "-U " : ""}${SANDBOX_PACKAGE_NAME}`));
|
|
684
721
|
}
|
|
685
722
|
console.log("");
|
|
686
723
|
return;
|
|
@@ -690,27 +727,40 @@ async function installPythonPackages(answers, upgrade) {
|
|
|
690
727
|
packages.push(MCP_PACKAGE_NAME);
|
|
691
728
|
}
|
|
692
729
|
if (answers.installRuntime) {
|
|
693
|
-
packages.push(
|
|
730
|
+
packages.push(SANDBOX_PACKAGE_NAME);
|
|
694
731
|
}
|
|
695
732
|
if (packages.length === 0) return;
|
|
696
733
|
const spinner = ora(
|
|
697
734
|
`${upgrade ? "Upgrading" : "Installing"} ${packages.map(stripExtras).join(", ")}...`
|
|
698
735
|
).start();
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
736
|
+
const attempts = [packages];
|
|
737
|
+
const failures = [];
|
|
738
|
+
for (const attemptPackages of attempts) {
|
|
739
|
+
try {
|
|
740
|
+
const installCmd = pipCmd === "uv" ? `uv pip install ${upgrade ? "--upgrade " : ""}${attemptPackages.join(" ")}` : `${pipCmd} install ${upgrade ? "--upgrade " : ""}${attemptPackages.join(" ")}`;
|
|
741
|
+
execSync(installCmd, {
|
|
742
|
+
stdio: "pipe",
|
|
743
|
+
encoding: "utf-8"
|
|
744
|
+
});
|
|
745
|
+
spinner.succeed(
|
|
746
|
+
`${upgrade ? "Upgraded" : "Installed"} ${attemptPackages.map(stripExtras).join(", ")}`
|
|
747
|
+
);
|
|
748
|
+
return;
|
|
749
|
+
} catch (error) {
|
|
750
|
+
const detail = error instanceof Error && "message" in error ? error.message.split("\n")[0] : "unknown error";
|
|
751
|
+
failures.push(`${attemptPackages.join(" ")}: ${detail}`);
|
|
752
|
+
}
|
|
713
753
|
}
|
|
754
|
+
spinner.fail(`Failed to ${upgrade ? "upgrade" : "install"} Python packages`);
|
|
755
|
+
if (failures.length > 0) {
|
|
756
|
+
console.log(chalk.yellow("\n Attempt summary:"));
|
|
757
|
+
for (const failure of failures) {
|
|
758
|
+
console.log(chalk.gray(` - ${failure}`));
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
console.log(chalk.yellow("\n Install manually with:"));
|
|
762
|
+
console.log(chalk.gray(` pip install ${upgrade ? "--upgrade " : ""}${packages.join(" ")}
|
|
763
|
+
`));
|
|
714
764
|
}
|
|
715
765
|
function stripExtras(name) {
|
|
716
766
|
return name.replace(/\[.*\]$/, "");
|
|
@@ -753,14 +803,15 @@ async function installNodeCompanion(upgrade) {
|
|
|
753
803
|
stdio: "pipe",
|
|
754
804
|
encoding: "utf-8"
|
|
755
805
|
});
|
|
756
|
-
|
|
806
|
+
const installedCommand = detectCompanionCommand();
|
|
807
|
+
if (installedCommand) {
|
|
757
808
|
spinner.succeed(
|
|
758
809
|
`${upgrade ? "Upgraded" : "Installed"} ${COMPANION_PACKAGE_NAME} via ${packageManager}`
|
|
759
810
|
);
|
|
760
811
|
return;
|
|
761
812
|
}
|
|
762
813
|
failures.push(
|
|
763
|
-
`${packageManager}: command completed but
|
|
814
|
+
`${packageManager}: command completed but ${COMPANION_COMMAND_NAME} is still not available on PATH (legacy ${LEGACY_COMPANION_COMMAND_NAME} is also accepted)`
|
|
764
815
|
);
|
|
765
816
|
} catch (error) {
|
|
766
817
|
const detail = error instanceof Error && "message" in error ? error.message.split("\n")[0] : "unknown error";
|
|
@@ -802,7 +853,7 @@ async function createMcpConfig(answers) {
|
|
|
802
853
|
};
|
|
803
854
|
}
|
|
804
855
|
if (answers.installRuntime) {
|
|
805
|
-
mcpConfig.mcpServers[
|
|
856
|
+
mcpConfig.mcpServers[SANDBOX_SERVER_NAME] = {
|
|
806
857
|
type: "http",
|
|
807
858
|
url: "http://localhost:8765/mcp",
|
|
808
859
|
headers: {}
|
|
@@ -887,22 +938,26 @@ function buildAgentsMdTemplate(answers) {
|
|
|
887
938
|
This project uses Snipara Hosted MCP for project context and reviewed memory.
|
|
888
939
|
|
|
889
940
|
- Endpoint: \`${buildHostedMcpEndpoint(answers)}\`
|
|
890
|
-
- Auth: use \`SNIPARA_API_KEY\`
|
|
941
|
+
- Auth: use \`SNIPARA_API_KEY\` from the environment. Never commit keys.
|
|
891
942
|
|
|
892
943
|
Agent task lifecycle:
|
|
893
944
|
|
|
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.
|
|
902
|
-
9. Use
|
|
903
|
-
10.
|
|
904
|
-
11.
|
|
905
|
-
12.
|
|
945
|
+
1. Start substantial work with \`snipara_recall\` and a targeted \`snipara_context_query\`.
|
|
946
|
+
2. Use \`snipara_context_query\` for docs, business context, client/project truth, architecture notes, runbooks, and narrative source material.
|
|
947
|
+
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.
|
|
948
|
+
4. Use \`snipara_get_chunk\` to load cited source sections returned by reference-based queries before relying on precise wording.
|
|
949
|
+
5. Use \`snipara_code_callers\`, \`snipara_code_imports\`, \`snipara_code_neighbors\`, or \`snipara_code_shortest_path\` for structural code questions.
|
|
950
|
+
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.
|
|
951
|
+
7. Use \`snipara_plan\` or \`snipara_decompose\` only for complex work and only when the hosted server exposes them.
|
|
952
|
+
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.
|
|
953
|
+
9. Use local file reads, \`rg\`, git commands, and test commands for exact edits, current working-tree state, and verification.
|
|
954
|
+
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.
|
|
955
|
+
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.
|
|
956
|
+
12. Use Snipara Sandbox only when sandboxed execution, repeatable validation, or isolated transformations materially help.
|
|
957
|
+
13. If a broad query is slow, retry once with a narrow keyword query before falling back to local search.
|
|
958
|
+
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\`.
|
|
959
|
+
15. Use \`snipara_remember_if_novel\` for one reusable memory while avoiding duplicates, and use \`snipara_remember\` for explicit direct memory writes.
|
|
960
|
+
16. Do not store secrets, tokens, passwords, private keys, raw logs, transient command output, or unreviewed guesses in memory.
|
|
906
961
|
`;
|
|
907
962
|
}
|
|
908
963
|
function buildClaudeMdTemplate(answers) {
|
|
@@ -915,13 +970,17 @@ Canonical project context is in \`AGENTS.md\`. This file exists for Claude Code
|
|
|
915
970
|
Use Snipara Hosted MCP before answering project-specific questions.
|
|
916
971
|
|
|
917
972
|
- Endpoint: \`${buildHostedMcpEndpoint(answers)}\`
|
|
918
|
-
- Start substantial work with \`
|
|
919
|
-
- Use \`
|
|
920
|
-
- When \`
|
|
921
|
-
- Use \`
|
|
922
|
-
- On paid Context plans, use \`
|
|
923
|
-
-
|
|
924
|
-
-
|
|
973
|
+
- Start substantial work with \`snipara_recall\` and a targeted \`snipara_context_query\`.
|
|
974
|
+
- Use \`snipara_context_query\` for source truth and \`snipara_get_chunk\` for exact cited sections.
|
|
975
|
+
- When \`snipara_context_query\` returns \`answer_pack\`, use its facts, caveats, source map, and verification checklist before drafting claims.
|
|
976
|
+
- Use \`snipara_code_callers\`, \`snipara_code_imports\`, \`snipara_code_neighbors\`, or \`snipara_code_shortest_path\` for structural code questions.
|
|
977
|
+
- 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.
|
|
978
|
+
- 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.
|
|
979
|
+
- 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.
|
|
980
|
+
- 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.
|
|
981
|
+
- Use Snipara Sandbox only when sandboxed execution or repeatable validation materially helps.
|
|
982
|
+
- 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\`.
|
|
983
|
+
- Use \`snipara_remember_if_novel\` or \`snipara_remember\` only for narrow durable memories.
|
|
925
984
|
- Do not store secrets, one-off command output, raw logs, or unreviewed guesses in memory.
|
|
926
985
|
- Use the Snipara Claude Code plugin only when slash commands, skills, or hooks are helpful; Hosted MCP remains the normal agent path.
|
|
927
986
|
`;
|
|
@@ -938,16 +997,18 @@ This workspace uses Snipara Hosted MCP for current project context, business/cli
|
|
|
938
997
|
|
|
939
998
|
- Check the configured Snipara MCP server before answering project-specific questions.
|
|
940
999
|
- Endpoint: \`${buildHostedMcpEndpoint(answers)}\`
|
|
941
|
-
- Use \`
|
|
942
|
-
- Use \`
|
|
943
|
-
- When \`
|
|
944
|
-
- Use \`
|
|
945
|
-
- Use \`
|
|
946
|
-
- On paid Context plans, use \`
|
|
947
|
-
-
|
|
948
|
-
- Use
|
|
949
|
-
- Use
|
|
950
|
-
-
|
|
1000
|
+
- Use \`snipara_recall\` for durable decisions, preferences, and validated learnings.
|
|
1001
|
+
- Use \`snipara_context_query\` for docs, business context, current client/project truth, and narrative source material.
|
|
1002
|
+
- When \`snipara_context_query\` returns \`answer_pack\`, use its facts, caveats, source map, and verification checklist before drafting claims.
|
|
1003
|
+
- Use \`snipara_get_chunk\` before relying on exact cited wording from reference-based results.
|
|
1004
|
+
- Use \`snipara_code_callers\`, \`snipara_code_imports\`, \`snipara_code_neighbors\`, or \`snipara_code_shortest_path\` for structural code questions.
|
|
1005
|
+
- 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.
|
|
1006
|
+
- 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.
|
|
1007
|
+
- Use \`snipara_plan\` or \`snipara_decompose\` only for complex work and only when available.
|
|
1008
|
+
- Use local file reads, \`rg\`, \`git status --short\`, and tests for exact code edits and current working-tree state.
|
|
1009
|
+
- 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.
|
|
1010
|
+
- Use Snipara Sandbox only when sandboxed execution or repeatable validation materially helps.
|
|
1011
|
+
- 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.
|
|
951
1012
|
`;
|
|
952
1013
|
}
|
|
953
1014
|
function buildCodexConfigTemplate(answers) {
|
|
@@ -1012,8 +1073,8 @@ async function createClaudeDesktopConfig(answers) {
|
|
|
1012
1073
|
}
|
|
1013
1074
|
};
|
|
1014
1075
|
if (answers.installRuntime) {
|
|
1015
|
-
config.mcpServers[
|
|
1016
|
-
command:
|
|
1076
|
+
config.mcpServers[SANDBOX_SERVER_NAME] = {
|
|
1077
|
+
command: detectSandboxCommand() || SANDBOX_COMMAND_NAME,
|
|
1017
1078
|
args: ["mcp-serve"],
|
|
1018
1079
|
env: {
|
|
1019
1080
|
SNIPARA_API_KEY: answers.apiKey,
|
|
@@ -1107,8 +1168,19 @@ set -euo pipefail
|
|
|
1107
1168
|
export SNIPARA_PROJECT_SLUG="${answers.projectSlug}"
|
|
1108
1169
|
${answers.projectId ? `export SNIPARA_PROJECT_ID="${answers.projectId}"` : ""}
|
|
1109
1170
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1171
|
+
SNIPARA_COMPANION_BIN=""
|
|
1172
|
+
if command -v ${COMPANION_COMMAND_NAME} >/dev/null 2>&1; then
|
|
1173
|
+
SNIPARA_COMPANION_BIN="${COMPANION_COMMAND_NAME}"
|
|
1174
|
+
elif command -v ${LEGACY_COMPANION_COMMAND_NAME} >/dev/null 2>&1; then
|
|
1175
|
+
SNIPARA_COMPANION_BIN="${LEGACY_COMPANION_COMMAND_NAME}"
|
|
1176
|
+
fi
|
|
1177
|
+
|
|
1178
|
+
if [ -n "$SNIPARA_COMPANION_BIN" ]; then
|
|
1179
|
+
if [ -f ".snipara/workflow/current.json" ]; then
|
|
1180
|
+
"$SNIPARA_COMPANION_BIN" workflow resume --include-session-context || true
|
|
1181
|
+
else
|
|
1182
|
+
"$SNIPARA_COMPANION_BIN" session-bootstrap || true
|
|
1183
|
+
fi
|
|
1112
1184
|
fi
|
|
1113
1185
|
`;
|
|
1114
1186
|
}
|
|
@@ -1119,8 +1191,15 @@ set -euo pipefail
|
|
|
1119
1191
|
export SNIPARA_PROJECT_SLUG="${answers.projectSlug}"
|
|
1120
1192
|
${answers.projectId ? `export SNIPARA_PROJECT_ID="${answers.projectId}"` : ""}
|
|
1121
1193
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1194
|
+
SNIPARA_COMPANION_BIN=""
|
|
1195
|
+
if command -v ${COMPANION_COMMAND_NAME} >/dev/null 2>&1; then
|
|
1196
|
+
SNIPARA_COMPANION_BIN="${COMPANION_COMMAND_NAME}"
|
|
1197
|
+
elif command -v ${LEGACY_COMPANION_COMMAND_NAME} >/dev/null 2>&1; then
|
|
1198
|
+
SNIPARA_COMPANION_BIN="${LEGACY_COMPANION_COMMAND_NAME}"
|
|
1199
|
+
fi
|
|
1200
|
+
|
|
1201
|
+
if [ -n "$SNIPARA_COMPANION_BIN" ]; then
|
|
1202
|
+
"$SNIPARA_COMPANION_BIN" emit-event --event-type tool_result --json '{"source":"claude-code-hook"}' || true
|
|
1124
1203
|
fi
|
|
1125
1204
|
`;
|
|
1126
1205
|
}
|
|
@@ -1131,8 +1210,19 @@ set -euo pipefail
|
|
|
1131
1210
|
export SNIPARA_PROJECT_SLUG="${answers.projectSlug}"
|
|
1132
1211
|
${answers.projectId ? `export SNIPARA_PROJECT_ID="${answers.projectId}"` : ""}
|
|
1133
1212
|
|
|
1134
|
-
|
|
1135
|
-
|
|
1213
|
+
SNIPARA_COMPANION_BIN=""
|
|
1214
|
+
if command -v ${COMPANION_COMMAND_NAME} >/dev/null 2>&1; then
|
|
1215
|
+
SNIPARA_COMPANION_BIN="${COMPANION_COMMAND_NAME}"
|
|
1216
|
+
elif command -v ${LEGACY_COMPANION_COMMAND_NAME} >/dev/null 2>&1; then
|
|
1217
|
+
SNIPARA_COMPANION_BIN="${LEGACY_COMPANION_COMMAND_NAME}"
|
|
1218
|
+
fi
|
|
1219
|
+
|
|
1220
|
+
if [ -n "$SNIPARA_COMPANION_BIN" ]; then
|
|
1221
|
+
if [ -f ".snipara/workflow/current.json" ]; then
|
|
1222
|
+
"$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
|
|
1223
|
+
else
|
|
1224
|
+
"$SNIPARA_COMPANION_BIN" task-commit --summary "Claude Code compacted local session context" --outcome partial || true
|
|
1225
|
+
fi
|
|
1136
1226
|
fi
|
|
1137
1227
|
`;
|
|
1138
1228
|
}
|
|
@@ -1154,7 +1244,7 @@ ${answers.projectId ? `SNIPARA_PROJECT_ID=${answers.projectId}` : ""}
|
|
|
1154
1244
|
const llmVarName = answers.llmProvider === "openai" ? "OPENAI_API_KEY" : "ANTHROPIC_API_KEY";
|
|
1155
1245
|
envVars += `
|
|
1156
1246
|
|
|
1157
|
-
#
|
|
1247
|
+
# Snipara Sandbox LLM Provider (for snipara-sandbox run/agent CLI)
|
|
1158
1248
|
${llmVarName}=${answers.llmApiKey}`;
|
|
1159
1249
|
}
|
|
1160
1250
|
envVars = envVars.trim();
|
|
@@ -1195,7 +1285,7 @@ ${envVars}
|
|
|
1195
1285
|
function getClientGuidance(client) {
|
|
1196
1286
|
if (client === "claude-code") {
|
|
1197
1287
|
return [
|
|
1198
|
-
"Claude Code can use both the hosted MCP surface and the local
|
|
1288
|
+
"Claude Code can use both the hosted MCP surface and the local snipara-companion flows.",
|
|
1199
1289
|
"If hooks are enabled, the edge layer forwards lifecycle signals without owning durable memory locally."
|
|
1200
1290
|
];
|
|
1201
1291
|
}
|
|
@@ -1230,30 +1320,30 @@ function getClientGuidance(client) {
|
|
|
1230
1320
|
}
|
|
1231
1321
|
function getCompanionCommands(answers) {
|
|
1232
1322
|
const projectIdFlag = answers.projectId ? ` --project-id ${answers.projectId}` : "";
|
|
1233
|
-
const apiKeyFlag = answers.apiKeyType === "none" || answers.apiKey.includes("YOUR_") ? " --api-key <
|
|
1323
|
+
const apiKeyFlag = answers.apiKeyType === "none" || answers.apiKey.includes("YOUR_") ? " --api-key <rlm-key>" : ` --api-key ${answers.apiKey}`;
|
|
1234
1324
|
const clientFlag = getCompanionInitClientFlag(answers.aiClient);
|
|
1235
1325
|
const businessOnboardingCommands = answers.installCompanion ? [
|
|
1236
1326
|
{
|
|
1237
1327
|
title: "Preview a business folder import",
|
|
1238
|
-
command:
|
|
1328
|
+
command: `${COMPANION_COMMAND_NAME} onboard-folder ./client-export --source-provider local_folder --write-manifest ./snipara-onboard.json`,
|
|
1239
1329
|
purpose: "Classify a local or LLM-materialized business folder and review the Snipara sync manifest before upload."
|
|
1240
1330
|
},
|
|
1241
1331
|
{
|
|
1242
1332
|
title: "Apply a business folder import",
|
|
1243
|
-
command:
|
|
1333
|
+
command: `${COMPANION_COMMAND_NAME} onboard-folder ./client-export --source-provider local_folder --apply`,
|
|
1244
1334
|
purpose: "Upload approved business-context documents to hosted Snipara and trigger document reindexing."
|
|
1245
1335
|
}
|
|
1246
1336
|
] : [
|
|
1247
1337
|
{
|
|
1248
1338
|
title: "Enable business folder onboarding",
|
|
1249
1339
|
command: "npx create-snipara repair --with-companion",
|
|
1250
|
-
purpose:
|
|
1340
|
+
purpose: `Install ${COMPANION_PACKAGE_NAME} so this project can run ${COMPANION_COMMAND_NAME} onboard-folder without using the dashboard.`
|
|
1251
1341
|
}
|
|
1252
1342
|
];
|
|
1253
1343
|
return [
|
|
1254
1344
|
{
|
|
1255
1345
|
title: "Bootstrap local companion",
|
|
1256
|
-
command:
|
|
1346
|
+
command: `${COMPANION_COMMAND_NAME} init${clientFlag}${projectIdFlag}${apiKeyFlag}`,
|
|
1257
1347
|
purpose: "Write local companion config for this workstation."
|
|
1258
1348
|
},
|
|
1259
1349
|
{
|
|
@@ -1268,44 +1358,69 @@ function getCompanionCommands(answers) {
|
|
|
1268
1358
|
},
|
|
1269
1359
|
{
|
|
1270
1360
|
title: "Run a focused hosted query",
|
|
1271
|
-
command:
|
|
1361
|
+
command: `${COMPANION_COMMAND_NAME} query --query "how does auth middleware work?" --max-tokens 4000`,
|
|
1272
1362
|
purpose: "Pull ranked source context before editing, reviewing, or drafting."
|
|
1273
1363
|
},
|
|
1274
1364
|
{
|
|
1275
1365
|
title: "Load linked standards only when needed",
|
|
1276
|
-
command:
|
|
1366
|
+
command: `${COMPANION_COMMAND_NAME} shared-context --categories MANDATORY BEST_PRACTICES`,
|
|
1277
1367
|
purpose: "Fetch project-linked team or workspace guidance without treating it as memory."
|
|
1278
1368
|
},
|
|
1279
1369
|
{
|
|
1280
1370
|
title: "Generate a hosted plan",
|
|
1281
|
-
command:
|
|
1371
|
+
command: `${COMPANION_COMMAND_NAME} plan --query "Implement webhook retry hardening" --max-tokens 3000`,
|
|
1282
1372
|
purpose: "Produce a structured execution plan against indexed project knowledge."
|
|
1283
1373
|
},
|
|
1374
|
+
{
|
|
1375
|
+
title: "Start a managed workflow from the visible plan",
|
|
1376
|
+
command: `${COMPANION_COMMAND_NAME} workflow start --goal "Implement webhook retry hardening" --plan-file ./plan.md`,
|
|
1377
|
+
purpose: "Record the LLM-visible plan in .snipara/workflow/current.json so phase state survives compaction."
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
title: "Start the current workflow phase",
|
|
1381
|
+
command: `${COMPANION_COMMAND_NAME} workflow phase-start context`,
|
|
1382
|
+
purpose: "Mark a phase active and print the required Snipara context gate for the agent."
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
title: "Analyze code impact before a risky change",
|
|
1386
|
+
command: `${COMPANION_COMMAND_NAME} code impact --changed-files src/api/webhooks.ts tests/webhooks.test.ts --diff-summary "webhook retry hardening"`,
|
|
1387
|
+
purpose: "Use paid Context code impact analysis to surface likely tests, docs, routes, config, and risks."
|
|
1388
|
+
},
|
|
1284
1389
|
{
|
|
1285
1390
|
title: "Upload or refresh a doc",
|
|
1286
|
-
command:
|
|
1391
|
+
command: `${COMPANION_COMMAND_NAME} upload --path docs/local-playbook.md --file ./docs/local-playbook.md`,
|
|
1287
1392
|
purpose: "Push local notes or docs into hosted Snipara context."
|
|
1288
1393
|
},
|
|
1289
1394
|
...businessOnboardingCommands,
|
|
1290
1395
|
{
|
|
1291
1396
|
title: "Inspect a specific chunk",
|
|
1292
|
-
command:
|
|
1397
|
+
command: `${COMPANION_COMMAND_NAME} chunk get --chunk-id <chunk_id>`,
|
|
1293
1398
|
purpose: "Open an exact chunk returned by references-based search."
|
|
1294
1399
|
},
|
|
1295
1400
|
{
|
|
1296
1401
|
title: "Bootstrap the session memory view",
|
|
1297
|
-
command:
|
|
1402
|
+
command: `${COMPANION_COMMAND_NAME} session-bootstrap`,
|
|
1298
1403
|
purpose: "Load durable decisions, learnings, and optional short-lived carryover at session start."
|
|
1299
1404
|
},
|
|
1300
1405
|
{
|
|
1301
1406
|
title: "Ask memory about past decisions",
|
|
1302
|
-
command:
|
|
1407
|
+
command: `${COMPANION_COMMAND_NAME} recall --query "What did we decide about auth retries?" --type decision`,
|
|
1303
1408
|
purpose: "Query durable memory for past decisions, preferences, or validated learnings."
|
|
1304
1409
|
},
|
|
1305
1410
|
{
|
|
1306
1411
|
title: "Commit durable task memory",
|
|
1307
|
-
command:
|
|
1412
|
+
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`,
|
|
1308
1413
|
purpose: "Persist reusable outcomes back into hosted memory after the work is done."
|
|
1414
|
+
},
|
|
1415
|
+
{
|
|
1416
|
+
title: "Commit a managed workflow phase",
|
|
1417
|
+
command: `${COMPANION_COMMAND_NAME} workflow phase-commit context --summary "Loaded context and mapped impacted files" --files src/api/webhooks.ts tests/webhooks.test.ts`,
|
|
1418
|
+
purpose: "Persist this phase through hosted memory and advance the local managed workflow state."
|
|
1419
|
+
},
|
|
1420
|
+
{
|
|
1421
|
+
title: "Commit the final managed workflow result",
|
|
1422
|
+
command: `${COMPANION_COMMAND_NAME} final-commit --summary "Shipped webhook retry hardening and tests" --files src/api/webhooks.ts tests/webhooks.test.ts`,
|
|
1423
|
+
purpose: "Persist the final workflow outcome and close the managed workflow after verification."
|
|
1309
1424
|
}
|
|
1310
1425
|
];
|
|
1311
1426
|
}
|
|
@@ -1318,7 +1433,7 @@ async function createCompanionPack(answers) {
|
|
|
1318
1433
|
fs.mkdirSync(packDir, { recursive: true });
|
|
1319
1434
|
const readme = `# Snipara Companion Pack
|
|
1320
1435
|
|
|
1321
|
-
This project uses Snipara as a hosted-first context and memory layer with
|
|
1436
|
+
This project uses Snipara as a hosted-first context and memory layer with local companion workflow helpers.
|
|
1322
1437
|
|
|
1323
1438
|
## Install Profile
|
|
1324
1439
|
|
|
@@ -1329,7 +1444,7 @@ This project uses Snipara as a hosted-first context and memory layer with an opt
|
|
|
1329
1444
|
- Installed local companion: \`${answers.installCompanion ? "yes" : "no"}\`
|
|
1330
1445
|
- Generated hosted MCP config: \`${answers.profile !== "runtime-only" ? "yes" : "no"}\`
|
|
1331
1446
|
- Installed local snipara-mcp package: \`${answers.installMcp ? "yes" : "no"}\`
|
|
1332
|
-
- Installed
|
|
1447
|
+
- Installed Snipara Sandbox: \`${answers.installRuntime ? "yes" : "no"}\`
|
|
1333
1448
|
- Generated hooks: \`${answers.installHooks ? "yes" : "no"}\`
|
|
1334
1449
|
|
|
1335
1450
|
## Local Detection Snapshot
|
|
@@ -1338,7 +1453,7 @@ This project uses Snipara as a hosted-first context and memory layer with an opt
|
|
|
1338
1453
|
- .mcp.json present: \`${detection.files.mcpConfig ? "yes" : "no"}\`
|
|
1339
1454
|
- .env.local present: \`${detection.files.envLocal ? "yes" : "no"}\`
|
|
1340
1455
|
- Companion CLI on PATH: \`${detection.tooling.companionCli ? "yes" : "no"}\`
|
|
1341
|
-
-
|
|
1456
|
+
- Snipara Sandbox CLI on PATH: \`${detection.tooling.runtimeCli ? "yes" : "no"}\`
|
|
1342
1457
|
|
|
1343
1458
|
## Client Guidance
|
|
1344
1459
|
|
|
@@ -1350,11 +1465,11 @@ Use this when Claude, ChatGPT, Codex, or another LLM client has already material
|
|
|
1350
1465
|
|
|
1351
1466
|
${answers.installCompanion ? `Preview first:
|
|
1352
1467
|
|
|
1353
|
-
|
|
1468
|
+
\`${COMPANION_COMMAND_NAME} onboard-folder ./client-export --source-provider local_folder --write-manifest ./snipara-onboard.json\`
|
|
1354
1469
|
|
|
1355
1470
|
Apply only after reviewing the manifest:
|
|
1356
1471
|
|
|
1357
|
-
|
|
1472
|
+
\`${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:
|
|
1358
1473
|
|
|
1359
1474
|
\`npx create-snipara repair --with-companion\``}
|
|
1360
1475
|
|
|
@@ -1384,6 +1499,7 @@ ${item.purpose}`
|
|
|
1384
1499
|
|
|
1385
1500
|
- Context first: use hosted queries, shared-context, and document loads for source truth.
|
|
1386
1501
|
- Memory second: use session bootstrap and task commit for durable decisions, learnings, and preferences.
|
|
1502
|
+
- Managed workflow when a visible plan exists: \`workflow start\`, \`phase-start\`, \`phase-commit\`, \`workflow resume\`, and \`final-commit\` keep phase state durable across compaction.
|
|
1387
1503
|
- Durable memory, policy, and orchestration stay hosted in Snipara.
|
|
1388
1504
|
- The local companion is optional tooling for capture, bootstrap, upload, exploration, doctor, repair, and explicit task summaries.
|
|
1389
1505
|
`;
|
|
@@ -1405,30 +1521,36 @@ ${item.purpose}`
|
|
|
1405
1521
|
console.log(chalk.green(" \u2713 Created .snipara/companion/README.md"));
|
|
1406
1522
|
console.log(chalk.green(" \u2713 Created .snipara/companion/commands.json"));
|
|
1407
1523
|
}
|
|
1408
|
-
async function
|
|
1409
|
-
|
|
1524
|
+
async function runSandboxInit() {
|
|
1525
|
+
const sandboxCommand = detectSandboxCommand() || SANDBOX_COMMAND_NAME;
|
|
1526
|
+
console.log(chalk.bold("\n\u{1F527} Running Snipara Sandbox Configuration\n"));
|
|
1410
1527
|
console.log(
|
|
1411
|
-
chalk.dim(
|
|
1528
|
+
chalk.dim(
|
|
1529
|
+
` Starting '${sandboxCommand} init' - follow the prompts to configure execution environment...
|
|
1530
|
+
`
|
|
1531
|
+
)
|
|
1412
1532
|
);
|
|
1413
1533
|
return new Promise((resolve) => {
|
|
1414
|
-
const child = spawn(
|
|
1534
|
+
const child = spawn(sandboxCommand, ["init"], {
|
|
1415
1535
|
stdio: "inherit",
|
|
1416
1536
|
shell: true
|
|
1417
1537
|
});
|
|
1418
1538
|
child.on("close", (code) => {
|
|
1419
1539
|
if (code === 0) {
|
|
1420
|
-
console.log(chalk.green("\n \u2713
|
|
1540
|
+
console.log(chalk.green("\n \u2713 Snipara Sandbox configured successfully"));
|
|
1421
1541
|
} else if (code !== null) {
|
|
1422
1542
|
console.log(chalk.yellow(`
|
|
1423
|
-
\u26A0\uFE0F
|
|
1424
|
-
console.log(chalk.dim(
|
|
1543
|
+
\u26A0\uFE0F ${sandboxCommand} init exited with code ${code}`));
|
|
1544
|
+
console.log(chalk.dim(` You can run '${sandboxCommand} init' manually later.`));
|
|
1425
1545
|
}
|
|
1426
1546
|
resolve();
|
|
1427
1547
|
});
|
|
1428
1548
|
child.on("error", (error) => {
|
|
1429
1549
|
console.log(chalk.yellow(`
|
|
1430
|
-
\u26A0\uFE0F Could not run
|
|
1431
|
-
console.log(
|
|
1550
|
+
\u26A0\uFE0F Could not run ${sandboxCommand} init: ${error.message}`));
|
|
1551
|
+
console.log(
|
|
1552
|
+
chalk.dim(` Run '${SANDBOX_COMMAND_NAME} init' manually after installation completes.`)
|
|
1553
|
+
);
|
|
1432
1554
|
resolve();
|
|
1433
1555
|
});
|
|
1434
1556
|
});
|
|
@@ -1483,9 +1605,9 @@ async function runValidation(answers, includeHostedCheck) {
|
|
|
1483
1605
|
detail: answers.installCompanion && !detection.tooling.companionCli ? `${COMPANION_PACKAGE_NAME} not found on PATH` : "Companion CLI ready or not requested"
|
|
1484
1606
|
});
|
|
1485
1607
|
checks.push({
|
|
1486
|
-
name: "
|
|
1608
|
+
name: "sandbox_cli",
|
|
1487
1609
|
status: answers.installRuntime ? detection.tooling.runtimeCli ? "pass" : "warn" : "pass",
|
|
1488
|
-
detail: answers.installRuntime && !detection.tooling.runtimeCli ?
|
|
1610
|
+
detail: answers.installRuntime && !detection.tooling.runtimeCli ? `${SANDBOX_COMMAND_NAME} CLI not found on PATH` : "Snipara Sandbox CLI ready or not requested"
|
|
1489
1611
|
});
|
|
1490
1612
|
checks.push({
|
|
1491
1613
|
name: "companion_pack",
|
|
@@ -1581,14 +1703,14 @@ async function testHostedConnection(answers) {
|
|
|
1581
1703
|
}
|
|
1582
1704
|
];
|
|
1583
1705
|
const settingsResponse = await callHostedMcp(answers, "tools/call", {
|
|
1584
|
-
name: "
|
|
1706
|
+
name: "snipara_settings",
|
|
1585
1707
|
arguments: {}
|
|
1586
1708
|
});
|
|
1587
1709
|
if (settingsResponse.ok && !settingsResponse.data.error) {
|
|
1588
1710
|
checks.push({
|
|
1589
1711
|
name: "hosted_settings",
|
|
1590
1712
|
status: "pass",
|
|
1591
|
-
detail: "
|
|
1713
|
+
detail: "snipara_settings tool call succeeded"
|
|
1592
1714
|
});
|
|
1593
1715
|
} else {
|
|
1594
1716
|
checks.push({
|
|
@@ -1640,7 +1762,7 @@ async function callHostedMcp(answers, method, params) {
|
|
|
1640
1762
|
function describeHostedHttpFailure(status, bodyText) {
|
|
1641
1763
|
const detail = bodyText.trim().slice(0, 120);
|
|
1642
1764
|
if (status === 401 || status === 403) {
|
|
1643
|
-
return `Auth failed (${status}). Check the
|
|
1765
|
+
return `Auth failed (${status}). Check the API key and project slug.`;
|
|
1644
1766
|
}
|
|
1645
1767
|
if (status === 404) {
|
|
1646
1768
|
return "Endpoint or project slug was not found. Check the hosted MCP URL.";
|
|
@@ -1679,7 +1801,9 @@ function printSuccess(answers, report) {
|
|
|
1679
1801
|
);
|
|
1680
1802
|
}
|
|
1681
1803
|
if (answers.installRuntime) {
|
|
1682
|
-
console.log(
|
|
1804
|
+
console.log(
|
|
1805
|
+
chalk.green(` \u2713 ${stripExtras(SANDBOX_PACKAGE_NAME)}`) + chalk.dim(" - Safe code execution")
|
|
1806
|
+
);
|
|
1683
1807
|
}
|
|
1684
1808
|
if (answers.installCompanion) {
|
|
1685
1809
|
console.log(
|
|
@@ -1710,20 +1834,27 @@ function printSuccess(answers, report) {
|
|
|
1710
1834
|
printNextStep("Open .snipara/companion/README.md for local commands and repair flow.");
|
|
1711
1835
|
printNextStep("Run create-snipara doctor anytime you want a fresh local validation report.");
|
|
1712
1836
|
if (answers.installCompanion) {
|
|
1713
|
-
printNextStep("Use rlm-hook query / plan / upload / task-commit for explicit local workflows.");
|
|
1714
1837
|
printNextStep(
|
|
1715
|
-
|
|
1838
|
+
`${COMPANION_COMMAND_NAME} is the primary companion command; legacy ${LEGACY_COMPANION_COMMAND_NAME} remains compatible.`
|
|
1716
1839
|
);
|
|
1717
1840
|
printNextStep(
|
|
1718
|
-
|
|
1841
|
+
`Use ${COMPANION_COMMAND_NAME} query / plan / upload / task-commit for explicit local workflows.`
|
|
1842
|
+
);
|
|
1843
|
+
printNextStep(
|
|
1844
|
+
`For business folders, preview with: ${COMPANION_COMMAND_NAME} onboard-folder ./client-export --source-provider local_folder --write-manifest ./snipara-onboard.json`
|
|
1845
|
+
);
|
|
1846
|
+
printNextStep(
|
|
1847
|
+
`Apply after review with: ${COMPANION_COMMAND_NAME} onboard-folder ./client-export --source-provider local_folder --apply`
|
|
1719
1848
|
);
|
|
1720
1849
|
} else if (answers.profile !== "runtime-only") {
|
|
1721
1850
|
printNextStep(
|
|
1722
1851
|
"For dashboardless business folder imports, enable companion with: npx create-snipara repair --with-companion"
|
|
1723
1852
|
);
|
|
1724
1853
|
}
|
|
1725
|
-
if (answers.installRuntime && !answers.
|
|
1726
|
-
printNextStep(
|
|
1854
|
+
if (answers.installRuntime && !answers.runSandboxInit) {
|
|
1855
|
+
printNextStep(
|
|
1856
|
+
`Run ${SANDBOX_COMMAND_NAME} init when you want to choose sandbox/docker/local execution.`
|
|
1857
|
+
);
|
|
1727
1858
|
}
|
|
1728
1859
|
if (answers.githubRepo) {
|
|
1729
1860
|
printNextStep(
|
|
@@ -1749,11 +1880,11 @@ function printSuccess(answers, report) {
|
|
|
1749
1880
|
}
|
|
1750
1881
|
function commandExists(command) {
|
|
1751
1882
|
try {
|
|
1752
|
-
execSync(`${command} --version`, { stdio: "ignore", shell: "/bin/sh" });
|
|
1883
|
+
execSync(`${command} --version`, { stdio: "ignore", shell: "/bin/sh", timeout: 2500 });
|
|
1753
1884
|
return true;
|
|
1754
1885
|
} catch {
|
|
1755
1886
|
try {
|
|
1756
|
-
execSync(`command -v ${command}`, { stdio: "ignore", shell: "/bin/sh" });
|
|
1887
|
+
execSync(`command -v ${command}`, { stdio: "ignore", shell: "/bin/sh", timeout: 1e3 });
|
|
1757
1888
|
return true;
|
|
1758
1889
|
} catch {
|
|
1759
1890
|
return false;
|
|
@@ -1790,8 +1921,8 @@ function detectEnvironment() {
|
|
|
1790
1921
|
uv: commandExists("uv"),
|
|
1791
1922
|
docker: commandExists("docker"),
|
|
1792
1923
|
git: commandExists("git"),
|
|
1793
|
-
companionCli:
|
|
1794
|
-
runtimeCli:
|
|
1924
|
+
companionCli: Boolean(detectCompanionCommand()),
|
|
1925
|
+
runtimeCli: Boolean(detectSandboxCommand())
|
|
1795
1926
|
},
|
|
1796
1927
|
existingConfig: {
|
|
1797
1928
|
slug: existingConfig?.slug,
|
|
@@ -1804,8 +1935,13 @@ function detectEnvironment() {
|
|
|
1804
1935
|
function loadExistingAnswers(options) {
|
|
1805
1936
|
const detection = detectEnvironment();
|
|
1806
1937
|
const effectiveProfile = resolveMaintenanceProfile(detection, options.profile);
|
|
1807
|
-
const
|
|
1808
|
-
|
|
1938
|
+
const providedApiKey = options.apiKey?.trim();
|
|
1939
|
+
if (providedApiKey && !isManualApiKey(providedApiKey)) {
|
|
1940
|
+
throw new Error(manualApiKeyValidationMessage());
|
|
1941
|
+
}
|
|
1942
|
+
const envApiKey = readApiKeyFromEnvFiles();
|
|
1943
|
+
const apiKey = providedApiKey || envApiKey || "YOUR_API_KEY_HERE";
|
|
1944
|
+
const apiKeyType = effectiveProfile === "runtime-only" ? "none" : providedApiKey || envApiKey ? "api" : "none";
|
|
1809
1945
|
return buildAnswersFromProfile({
|
|
1810
1946
|
projectSlug: options.slug || detection.existingConfig.slug || detection.projectSlug || "my-project",
|
|
1811
1947
|
projectId: options.projectId || detection.existingConfig.projectId || "",
|
|
@@ -1832,7 +1968,7 @@ function resolveMaintenanceProfile(detection, explicitProfile) {
|
|
|
1832
1968
|
if (detection.existingConfig.hasSniparaServer) {
|
|
1833
1969
|
return "hosted-only";
|
|
1834
1970
|
}
|
|
1835
|
-
return "hosted-
|
|
1971
|
+
return "hosted-companion";
|
|
1836
1972
|
}
|
|
1837
1973
|
function readApiKeyFromEnvFiles() {
|
|
1838
1974
|
const candidates = [".env.local", ".env"];
|
|
@@ -1952,7 +2088,7 @@ function getExistingConfig() {
|
|
|
1952
2088
|
try {
|
|
1953
2089
|
const config = JSON.parse(fs.readFileSync(mcpConfigPath, "utf-8"));
|
|
1954
2090
|
const sniparaServer = config.mcpServers?.snipara;
|
|
1955
|
-
const runtimeServer = config.mcpServers?.[
|
|
2091
|
+
const runtimeServer = config.mcpServers?.[SANDBOX_SERVER_NAME] || config.mcpServers?.[LEGACY_RUNTIME_SERVER_NAME];
|
|
1956
2092
|
const match = sniparaServer?.url?.match(/\/mcp\/([^/]+)$/);
|
|
1957
2093
|
return {
|
|
1958
2094
|
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
|
|
3
|
+
"version": "1.3.12",
|
|
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",
|