create-snipara 1.3.8 → 1.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +78 -22
  2. package/dist/index.js +259 -76
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -1,12 +1,18 @@
1
1
  # create-snipara
2
2
 
3
- One-command onboarding and maintenance CLI for **Snipara Hosted MCP Context + Memory** with optional `snipara-companion` and optional RLM-Runtime.
3
+ **One-command onboarding for Snipara Hosted MCP Context + Memory.**
4
+
5
+ `create-snipara` connects a local project to Snipara so AI agents can start
6
+ with project-owned context, reviewed memory, shared guidance, and optional code
7
+ impact tooling instead of starting cold every session.
4
8
 
5
9
  ```bash
6
10
  npx create-snipara
7
11
  ```
8
12
 
9
- `create-snipara` is the simple onboarding package for the agent path. It creates or reuses auth, writes Hosted MCP config, and prepares the local project so an LLM client can use Snipara context and reviewed memory.
13
+ `create-snipara` is the simple onboarding package for the agent path. It
14
+ creates or reuses auth, writes Hosted MCP config, and prepares the local project
15
+ so an LLM client can use Snipara context and reviewed memory.
10
16
 
11
17
  Use it for:
12
18
 
@@ -15,6 +21,26 @@ Use it for:
15
21
  - local project wiring, `doctor`, `repair`, and `upgrade`
16
22
  - GitHub repository sync approval when the current folder is a GitHub repo
17
23
 
24
+ ## Why It Exists
25
+
26
+ AI clients all have slightly different config formats, but the project need is
27
+ the same: connect the agent to the same durable project memory. `create-snipara`
28
+ normalizes that setup.
29
+
30
+ ```mermaid
31
+ flowchart LR
32
+ Project["Local project"] --> CLI["npx create-snipara"]
33
+ CLI --> Auth["Snipara auth and project key"]
34
+ CLI --> MCP["Hosted MCP config"]
35
+ CLI --> Templates["AGENTS.md, CLAUDE.md, Cursor, Codex snippets"]
36
+ MCP --> Agents["Codex, Claude Code, Cursor, ChatGPT Desktop"]
37
+ Templates --> Agents
38
+ Agents --> Context["Snipara context and memory"]
39
+ ```
40
+
41
+ The CLI does not become the runtime brain. After setup, the agent talks to
42
+ Hosted MCP and keeps using its own LLM.
43
+
18
44
  Do not use it to choose business documents one by one. For local folders, mounted Drive/SharePoint exports, old offers, or PowerPoints, use `snipara-business`.
19
45
 
20
46
  The product surfaces remain:
@@ -22,9 +48,9 @@ The product surfaces remain:
22
48
  - Hosted MCP for LLM agents, context, and reviewed memory
23
49
  - HTTP API / SDK for apps, integrators, backends, and pipelines
24
50
 
25
- The CLI can wire local helpers around those surfaces:
51
+ The default CLI path wires the local companion around those surfaces:
26
52
 
27
- - `snipara-companion` for local `rlm-hook` workflows when you want them
53
+ - `snipara-companion` for local `rlm-hook` workflows and managed phase commits
28
54
  - `rlm-runtime` for optional local code execution
29
55
  - local config, env files, hook scripts, and a project companion pack
30
56
  - `snipara-mcp` only for local stdio compatibility, development, or testing
@@ -35,12 +61,12 @@ The CLI now works around explicit install profiles:
35
61
 
36
62
  | Profile | What it installs | Best for |
37
63
  | ------------------ | ----------------------------------------- | --------------------------------------------------- |
38
- | `hosted-only` | Hosted MCP config only | Default: agents without local helper workflows |
39
- | `hosted-companion` | MCP + `snipara-companion` | Hook-heavy clients that want local helper workflows |
40
- | `full-stack` | MCP + `snipara-companion` + `rlm-runtime` | Hosted context plus local execution |
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` + `rlm-runtime` | Hosted context plus sandboxed local execution |
41
67
  | `runtime-only` | `rlm-runtime` only | Local execution without hosted API |
42
68
 
43
- Default profile: `hosted-only`
69
+ Default profile: `hosted-companion`
44
70
 
45
71
  ## Free Account Fit
46
72
 
@@ -73,10 +99,10 @@ That distinction matters:
73
99
  - opens the browser device-flow free Context + Memory account flow when needed
74
100
  - writes Hosted MCP configuration
75
101
  - detects a GitHub remote and can open the Snipara GitHub App automation path
76
- - installs `snipara-companion` when the selected profile needs local helper workflows
102
+ - installs `snipara-companion` by default for local managed workflows
77
103
  - installs optional `rlm-runtime`
78
104
  - configures `.mcp.json`
79
- - generates merge-ready AI agent templates in `.snipara/templates/`, including Pro+ symbol-card and code-impact guidance for paid Context plans
105
+ - generates merge-ready AI agent templates in `.snipara/templates/`, including Pro+ symbol-card, code-impact, and managed workflow guidance for paid Context plans
80
106
  - creates `AGENTS.md`, `CLAUDE.md`, `.cursor/rules/snipara.mdc`, and `.codex/config.toml` when those files do not already exist
81
107
  - updates `.env`, `.env.local`, and `.env.example`
82
108
  - generates `.snipara/companion/README.md`
@@ -104,11 +130,19 @@ npx create-snipara
104
130
 
105
131
  The interactive flow now asks for:
106
132
 
107
- - install profile
108
133
  - project slug
109
- - optional project id
110
134
  - API key path: project key, team key, free signup, or add key later
111
135
  - AI client
136
+
137
+ By default, this uses the recommended `hosted-companion` setup: hosted MCP plus
138
+ the local `rlm-hook` workflow helper. Add `--advanced` when you want profile
139
+ selection, project id entry, hook prompts, GitHub automation prompts, or runtime
140
+ provider prompts.
141
+
142
+ The advanced flow asks for:
143
+
144
+ - install profile
145
+ - optional project id
112
146
  - whether hooks should be generated
113
147
  - whether to open GitHub repository automation when a GitHub remote is detected
114
148
  - optional runtime provider config when `rlm-runtime` is installed
@@ -121,6 +155,17 @@ The generated agent templates cover:
121
155
  - Codex `.codex/config.toml` snippet
122
156
  - ChatGPT/OpenAI HTTP MCP snippet
123
157
 
158
+ When `snipara-companion` is installed, the companion pack also includes the compaction-safe managed workflow:
159
+
160
+ ```bash
161
+ rlm-hook workflow start --goal "ship auth hardening" --plan-file ./plan.md
162
+ rlm-hook workflow phase-start context
163
+ rlm-hook workflow run --mode full --include-session-context --query "load auth context"
164
+ rlm-hook code impact --changed-files src/auth.ts tests/auth.test.ts --diff-summary "auth hardening"
165
+ rlm-hook workflow phase-commit context --summary "Loaded context and mapped impacted files" --files src/auth.ts
166
+ rlm-hook final-commit --summary "Shipped auth hardening and tests" --files src/auth.ts tests/auth.test.ts
167
+ ```
168
+
124
169
  ## Main Commands
125
170
 
126
171
  ### Install or initialize
@@ -214,9 +259,10 @@ That keeps business truth review separate from agent onboarding.
214
259
 
215
260
  ```bash
216
261
  # Use an explicit install profile
217
- npx create-snipara --profile hosted-only
218
262
  npx create-snipara --profile hosted-companion
263
+ npx create-snipara --profile hosted-only
219
264
  npx create-snipara --profile full-stack
265
+ npx create-snipara --advanced
220
266
 
221
267
  # Runtime-only local execution
222
268
  npx create-snipara --runtime-only
@@ -237,6 +283,7 @@ npx create-snipara --skip-github
237
283
 
238
284
  # Force extra layers during maintenance
239
285
  npx create-snipara repair --with-hooks
286
+ npx create-snipara repair --with-runtime
240
287
  npx create-snipara upgrade --with-runtime
241
288
  ```
242
289
 
@@ -245,18 +292,19 @@ npx create-snipara upgrade --with-runtime
245
292
  The intended split is:
246
293
 
247
294
  - `create-snipara`: Context + Memory onboarding, auth, config, inspect, repair, and upgrade
248
- - `snipara-companion`: optional ongoing local runtime workflows through `rlm-hook`
295
+ - `snipara-companion`: default local workflow helper through `rlm-hook`; optional only for `hosted-only`
249
296
  - `snipara-mcp`: local stdio bridge, client-package development, compatibility, and testing
250
297
  - `rlm-runtime`: optional local execution engine
251
298
 
252
299
  ## Codex Setup
253
300
 
254
- For Codex, keep the setup minimal:
301
+ For Codex, keep Hosted MCP as the core path and let companion add workflow
302
+ ergonomics:
255
303
 
256
304
  - use Hosted MCP as the core Snipara surface
257
305
  - use `AGENTS.md` for project instructions
258
- - choose `hosted-only` if you do not need local helper workflows
259
- - add `snipara-companion` only if you explicitly want the local helper CLI
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
260
308
  - use local `snipara-mcp` only for stdio compatibility, development, or tests
261
309
 
262
310
  Codex does not require hook presets to use Snipara successfully.
@@ -318,21 +366,29 @@ Recommended explanation to users:
318
366
 
319
367
  ## Example Flows
320
368
 
321
- ### Default hosted setup
369
+ ### Default managed hosted setup
370
+
371
+ ```bash
372
+ npx create-snipara
373
+ ```
374
+
375
+ Best when the user wants Hosted MCP Context + Memory plus `rlm-hook` managed workflows.
376
+
377
+ ### Minimal hosted setup
322
378
 
323
379
  ```bash
324
380
  npx create-snipara --profile hosted-only
325
381
  ```
326
382
 
327
- Best when the user only wants Hosted MCP Context + Memory.
383
+ Best when the user only wants Hosted MCP Context + Memory and cannot install local helper tooling.
328
384
 
329
385
  ### Full local stack
330
386
 
331
387
  ```bash
332
- npx create-snipara --profile full-stack
388
+ npx create-snipara --profile full-stack --advanced
333
389
  ```
334
390
 
335
- Best when the user also wants `snipara-companion` and `rlm-runtime`.
391
+ Best when the user also wants sandboxed `rlm-runtime` execution.
336
392
 
337
393
  ### Connect the current repository
338
394
 
@@ -377,7 +433,7 @@ npx create-snipara print-config --json
377
433
  When testing a locally packed tarball, use `npm exec --package` instead of `npx`, for example:
378
434
 
379
435
  ```bash
380
- npm exec --package ./create-snipara-1.3.8.tgz create-snipara -- --help
436
+ npm exec --package ./create-snipara-1.3.11.tgz create-snipara -- --help
381
437
  ```
382
438
 
383
439
  This avoids `npx` resolution issues with unpublished tarballs.
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import fs from "fs";
9
9
  import path from "path";
10
10
  import { execSync, spawn } from "child_process";
11
11
  import { homedir } from "os";
12
- var VERSION = "1.3.8";
12
+ var VERSION = "1.3.11";
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";
@@ -26,8 +26,8 @@ function getCompanionInitClientFlag(aiClient) {
26
26
  }
27
27
  function buildProgram() {
28
28
  const program = new Command();
29
- program.name("create-snipara").description("Scaffold hosted Snipara MCP config plus optional local helper workflows").version(VERSION);
30
- program.command("init").description("Install and configure Snipara for this project").option("--api-key <key>", "Snipara project API key").option("--team-key <key>", "Snipara team API key").option("--slug <slug>", "Project slug").option("--project-id <id>", "Project ID").option(
29
+ 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 project API key").option("--team-key <key>", "Snipara team API key").option("--slug <slug>", "Project slug").option("--project-id <id>", "Project ID").option("--advanced", "Show advanced profile, project id, hooks, GitHub, and runtime prompts").option(
31
31
  "--profile <profile>",
32
32
  "Install profile: hosted-only, hosted-companion, full-stack, runtime-only"
33
33
  ).option("--skip-companion", "Skip local companion CLI installation").option("--skip-mcp", "Skip local snipara-mcp package installation").option("--skip-runtime", "Skip rlm-runtime 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 RLM-Runtime").option("-y, --yes", "Accept defaults without prompting").action(async (options) => {
@@ -58,7 +58,7 @@ function buildProgram() {
58
58
  return program;
59
59
  }
60
60
  function addSharedRootOptions(program) {
61
- program.option("--api-key <key>", "Snipara project API key").option("--team-key <key>", "Snipara team API key").option("--slug <slug>", "Project slug").option("--project-id <id>", "Project ID").option(
61
+ program.option("--api-key <key>", "Snipara project API key").option("--team-key <key>", "Snipara team API key").option("--slug <slug>", "Project slug").option("--project-id <id>", "Project ID").option("--advanced", "Show advanced profile, project id, hooks, GitHub, and runtime prompts").option(
62
62
  "--profile <profile>",
63
63
  "Install profile: hosted-only, hosted-companion, full-stack, runtime-only"
64
64
  ).option("--skip-companion", "Skip local companion CLI installation").option("--skip-mcp", "Skip local snipara-mcp package installation").option("--skip-runtime", "Skip rlm-runtime 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 RLM-Runtime").option("-y, --yes", "Accept defaults without prompting");
@@ -67,8 +67,63 @@ async function main() {
67
67
  const program = buildProgram();
68
68
  await program.parseAsync(process.argv);
69
69
  }
70
+ function normalizeInitOptions(options) {
71
+ const parsed = parseInitOptionsFromArgv(process.argv.slice(2));
72
+ const normalized = { ...parsed };
73
+ for (const [key, value] of Object.entries(options)) {
74
+ if (value !== void 0) {
75
+ normalized[key] = value;
76
+ }
77
+ }
78
+ return normalized;
79
+ }
80
+ function parseInitOptionsFromArgv(args) {
81
+ const parsed = {};
82
+ const valueFlags = {
83
+ "--api-key": "apiKey",
84
+ "--team-key": "teamKey",
85
+ "--slug": "slug",
86
+ "--project-id": "projectId",
87
+ "--profile": "profile",
88
+ "--github-path": "githubPath"
89
+ };
90
+ const booleanFlags = {
91
+ "--advanced": "advanced",
92
+ "--skip-companion": "skipCompanion",
93
+ "--skip-mcp": "skipMcp",
94
+ "--skip-runtime": "skipRuntime",
95
+ "--skip-hooks": "skipHooks",
96
+ "--skip-test": "skipTest",
97
+ "--runtime-only": "runtimeOnly",
98
+ "--github": "github",
99
+ "--skip-github": "skipGithub",
100
+ "--yes": "yes",
101
+ "-y": "yes"
102
+ };
103
+ for (let index = 0; index < args.length; index += 1) {
104
+ const arg = args[index];
105
+ if (arg === "init") {
106
+ continue;
107
+ }
108
+ const [flag, inlineValue] = arg.split("=", 2);
109
+ if (flag in valueFlags) {
110
+ const value = inlineValue ?? args[index + 1];
111
+ if (value && !value.startsWith("-")) {
112
+ parsed[valueFlags[flag]] = value;
113
+ if (inlineValue === void 0) {
114
+ index += 1;
115
+ }
116
+ }
117
+ continue;
118
+ }
119
+ if (arg in booleanFlags) {
120
+ parsed[booleanFlags[arg]] = true;
121
+ }
122
+ }
123
+ return parsed;
124
+ }
70
125
  async function runInit(options) {
71
- const answers = await gatherConfiguration(options);
126
+ const answers = await gatherConfiguration(normalizeInitOptions(options));
72
127
  if (answers.installMcp || answers.installRuntime) {
73
128
  await installPythonPackages(answers, false);
74
129
  }
@@ -198,6 +253,7 @@ async function gatherConfiguration(options) {
198
253
  const detectedClient = detectAiClient();
199
254
  const existingConfig = getExistingConfig();
200
255
  const detectedGitRepo = detectGitRepository();
256
+ const showAdvancedPrompts = Boolean(options.advanced);
201
257
  if (options.github && !detectedGitRepo) {
202
258
  console.log(
203
259
  chalk.yellow(
@@ -230,7 +286,7 @@ async function gatherConfiguration(options) {
230
286
  }
231
287
  const response = await prompts([
232
288
  {
233
- type: "select",
289
+ type: showAdvancedPrompts ? "select" : null,
234
290
  name: "profile",
235
291
  message: "Choose an install profile:",
236
292
  initial: installProfileChoices().findIndex((choice) => choice.value === initialProfile),
@@ -244,7 +300,7 @@ async function gatherConfiguration(options) {
244
300
  validate: (value) => value.length > 0 ? true : "Project slug is required (for example my-project)"
245
301
  },
246
302
  {
247
- type: "text",
303
+ type: showAdvancedPrompts ? "text" : null,
248
304
  name: "projectId",
249
305
  message: "Project ID (optional, press Enter to skip):",
250
306
  initial: options.projectId || detectedProjectId || ""
@@ -318,19 +374,19 @@ async function gatherConfiguration(options) {
318
374
  ]
319
375
  },
320
376
  {
321
- type: "confirm",
377
+ type: showAdvancedPrompts ? "confirm" : null,
322
378
  name: "installHooks",
323
379
  message: "Generate local hooks when supported?",
324
380
  initial: detectedClient === "claude-code"
325
381
  },
326
382
  {
327
- type: (_, values) => detectedGitRepo && !options.skipGithub && values.profile !== "runtime-only" ? "confirm" : null,
383
+ type: (_, values) => detectedGitRepo && showAdvancedPrompts && !options.skipGithub && (values.profile || initialProfile) !== "runtime-only" ? "confirm" : null,
328
384
  name: "configureGitHub",
329
385
  message: detectedGitRepo ? `Open GitHub automation setup for ${detectedGitRepo.fullName} after local config?` : "Open GitHub automation setup after local config?",
330
386
  initial: true
331
387
  },
332
388
  {
333
- type: (_, values) => values.configureGitHub && detectedGitRepo ? "text" : null,
389
+ type: (_, values) => showAdvancedPrompts && values.configureGitHub && detectedGitRepo ? "text" : null,
334
390
  name: "githubPath",
335
391
  message: "GitHub path filter (optional, press Enter for Snipara defaults):",
336
392
  initial: options.githubPath || ""
@@ -354,7 +410,7 @@ async function gatherConfiguration(options) {
354
410
  }
355
411
  const answers = buildAnswersFromProfile({
356
412
  projectSlug: signupResult?.projectSlug || response.projectSlug,
357
- projectId: signupResult?.projectId || response.projectId || "",
413
+ projectId: signupResult?.projectId || response.projectId || options.projectId || "",
358
414
  apiKey: apiKey || "",
359
415
  apiKeyType,
360
416
  profile: response.profile || initialProfile,
@@ -427,27 +483,27 @@ function resolveRequestedProfile(options) {
427
483
  if (options.profile && isInstallProfile(options.profile)) {
428
484
  return options.profile;
429
485
  }
430
- return "hosted-only";
486
+ return "hosted-companion";
431
487
  }
432
488
  function isInstallProfile(value) {
433
489
  return ["hosted-only", "hosted-companion", "full-stack", "runtime-only"].includes(value);
434
490
  }
435
491
  function installProfileChoices() {
436
492
  return [
437
- {
438
- title: "Hosted only",
439
- value: "hosted-only",
440
- description: "Default: hosted MCP config, no local companion or runtime"
441
- },
442
493
  {
443
494
  title: "Hosted + Companion",
444
495
  value: "hosted-companion",
445
- description: "Hosted MCP config plus optional local companion workflows"
496
+ description: "Default: hosted MCP config plus managed local workflow commands"
497
+ },
498
+ {
499
+ title: "Hosted only",
500
+ value: "hosted-only",
501
+ description: "Minimal setup: hosted MCP config, no local companion or runtime"
446
502
  },
447
503
  {
448
504
  title: "Hosted + Companion + Runtime",
449
505
  value: "full-stack",
450
- description: "Add local runtime execution as well"
506
+ description: "Add sandboxed local runtime execution as well"
451
507
  },
452
508
  {
453
509
  title: "Runtime only",
@@ -837,15 +893,20 @@ Agent task lifecycle:
837
893
 
838
894
  1. Start substantial work with \`rlm_recall\` and a targeted \`rlm_context_query\`.
839
895
  2. Use \`rlm_context_query\` for docs, business context, client/project truth, architecture notes, runbooks, and narrative source material.
840
- 3. Use \`rlm_get_chunk\` to load cited source sections returned by reference-based queries before relying on precise wording.
841
- 4. Use \`rlm_code_callers\`, \`rlm_code_imports\`, \`rlm_code_neighbors\`, or \`rlm_code_shortest_path\` for structural code questions.
842
- 5. On paid Context plans, use \`rlm_code_symbol_card\` for agent-ready symbol context and \`rlm_code_impact\` before risky multi-file changes or PR reviews.
843
- 6. Use \`rlm_plan\` or \`rlm_decompose\` only for complex work and only when the hosted server exposes them.
844
- 7. Use local file reads, \`rg\`, git commands, and test commands for exact edits, current working-tree state, and verification.
845
- 8. Use RLM Runtime only when sandboxed execution, repeatable validation, or isolated transformations materially help.
846
- 9. If a broad query is slow, retry once with a narrow keyword query before falling back to local search.
847
- 10. End substantial work with \`rlm_end_of_task_commit\` when available. Use \`rlm_remember_if_novel\` for one reusable memory while avoiding duplicates, and use \`rlm_remember\` for explicit direct memory writes.
848
- 11. Do not store secrets, tokens, passwords, private keys, raw logs, transient command output, or unreviewed guesses in memory.
896
+ 3. When \`rlm_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.
897
+ 4. Use \`rlm_get_chunk\` to load cited source sections returned by reference-based queries before relying on precise wording.
898
+ 5. Use \`rlm_code_callers\`, \`rlm_code_imports\`, \`rlm_code_neighbors\`, or \`rlm_code_shortest_path\` for structural code questions.
899
+ 6. On paid Context plans, use \`rlm_code_symbol_card\` for agent-ready symbol context and \`rlm_code_impact\` before risky multi-file changes or PR reviews.
900
+ 7. Use \`rlm_plan\` or \`rlm_decompose\` only for complex work and only when the hosted server exposes them.
901
+ 8. When the LLM has produced a visible multi-phase plan and \`snipara-companion\` is installed, run \`rlm-hook 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.
902
+ 9. Use local file reads, \`rg\`, git commands, and test commands for exact edits, current working-tree state, and verification.
903
+ 10. Treat every \`rlm_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.
904
+ 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 RLM Runtime only when sandboxed execution, repeatable validation, or isolated transformations materially help.
906
+ 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 \`rlm_end_of_task_commit\` or \`rlm-hook workflow phase-commit\` when companion manages the plan. End the whole managed workflow with \`rlm-hook final-commit\`.
908
+ 15. Use \`rlm_remember_if_novel\` for one reusable memory while avoiding duplicates, and use \`rlm_remember\` for explicit direct memory writes.
909
+ 16. Do not store secrets, tokens, passwords, private keys, raw logs, transient command output, or unreviewed guesses in memory.
849
910
  `;
850
911
  }
851
912
  function buildClaudeMdTemplate(answers) {
@@ -860,10 +921,15 @@ Use Snipara Hosted MCP before answering project-specific questions.
860
921
  - Endpoint: \`${buildHostedMcpEndpoint(answers)}\`
861
922
  - Start substantial work with \`rlm_recall\` and a targeted \`rlm_context_query\`.
862
923
  - Use \`rlm_context_query\` for source truth and \`rlm_get_chunk\` for exact cited sections.
924
+ - When \`rlm_context_query\` returns \`answer_pack\`, use its facts, caveats, source map, and verification checklist before drafting claims.
863
925
  - Use \`rlm_code_callers\`, \`rlm_code_imports\`, \`rlm_code_neighbors\`, or \`rlm_code_shortest_path\` for structural code questions.
864
926
  - On paid Context plans, use \`rlm_code_symbol_card\` before editing important symbols and \`rlm_code_impact\` before risky multi-file changes or PR reviews.
927
+ - Check every \`rlm_code_*\` response for indexed commit SHA, indexing time, included files, coverage, and freshness warnings.
928
+ - 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 \`rlm-hook workflow start\`, \`phase-start\`, \`phase-commit\`, \`workflow resume\`, and \`final-commit\` to keep phase state durable across compaction.
865
930
  - Use RLM Runtime only when sandboxed execution or repeatable validation materially helps.
866
- - End substantial work with \`rlm_end_of_task_commit\` when available; use \`rlm_remember_if_novel\` or \`rlm_remember\` only for narrow durable memories.
931
+ - End substantial work with \`rlm_end_of_task_commit\` when available; for managed workflows, end every phase with \`rlm-hook workflow phase-commit\` and the task with \`rlm-hook final-commit\`.
932
+ - Use \`rlm_remember_if_novel\` or \`rlm_remember\` only for narrow durable memories.
867
933
  - Do not store secrets, one-off command output, raw logs, or unreviewed guesses in memory.
868
934
  - Use the Snipara Claude Code plugin only when slash commands, skills, or hooks are helpful; Hosted MCP remains the normal agent path.
869
935
  `;
@@ -882,13 +948,16 @@ This workspace uses Snipara Hosted MCP for current project context, business/cli
882
948
  - Endpoint: \`${buildHostedMcpEndpoint(answers)}\`
883
949
  - Use \`rlm_recall\` for durable decisions, preferences, and validated learnings.
884
950
  - Use \`rlm_context_query\` for docs, business context, current client/project truth, and narrative source material.
951
+ - When \`rlm_context_query\` returns \`answer_pack\`, use its facts, caveats, source map, and verification checklist before drafting claims.
885
952
  - Use \`rlm_get_chunk\` before relying on exact cited wording from reference-based results.
886
953
  - Use \`rlm_code_callers\`, \`rlm_code_imports\`, \`rlm_code_neighbors\`, or \`rlm_code_shortest_path\` for structural code questions.
887
954
  - On paid Context plans, use \`rlm_code_symbol_card\` before editing important symbols and \`rlm_code_impact\` before risky multi-file changes or PR reviews.
955
+ - Check every \`rlm_code_*\` response for indexed commit SHA, indexing time, included files, coverage, and freshness warnings.
888
956
  - Use \`rlm_plan\` or \`rlm_decompose\` only for complex work and only when available.
889
- - Use local file reads and tests for exact code edits and current working-tree state.
957
+ - 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 \`rlm-hook workflow start\`, \`phase-start\`, \`phase-commit\`, \`workflow resume\`, and \`final-commit\` so the next phase survives compaction.
890
959
  - Use RLM Runtime only when sandboxed execution or repeatable validation materially helps.
891
- - End substantial work with \`rlm_end_of_task_commit\` when available; use \`rlm_remember_if_novel\` or \`rlm_remember\` only for narrow durable memories. Never store secrets.
960
+ - End substantial work with \`rlm_end_of_task_commit\` when available; for managed workflows, end every phase with \`rlm-hook workflow phase-commit\` and the task with \`rlm-hook final-commit\`. Use \`rlm_remember_if_novel\` or \`rlm_remember\` only for narrow durable memories. Never store secrets.
892
961
  `;
893
962
  }
894
963
  function buildCodexConfigTemplate(answers) {
@@ -1049,7 +1118,11 @@ export SNIPARA_PROJECT_SLUG="${answers.projectSlug}"
1049
1118
  ${answers.projectId ? `export SNIPARA_PROJECT_ID="${answers.projectId}"` : ""}
1050
1119
 
1051
1120
  if command -v rlm-hook >/dev/null 2>&1; then
1052
- rlm-hook session-bootstrap || true
1121
+ if [ -f ".snipara/workflow/current.json" ]; then
1122
+ rlm-hook workflow resume --include-session-context || true
1123
+ else
1124
+ rlm-hook session-bootstrap || true
1125
+ fi
1053
1126
  fi
1054
1127
  `;
1055
1128
  }
@@ -1073,7 +1146,11 @@ export SNIPARA_PROJECT_SLUG="${answers.projectSlug}"
1073
1146
  ${answers.projectId ? `export SNIPARA_PROJECT_ID="${answers.projectId}"` : ""}
1074
1147
 
1075
1148
  if command -v rlm-hook >/dev/null 2>&1; then
1076
- rlm-hook task-commit --summary "Claude Code compacted local session context" --outcome partial || true
1149
+ if [ -f ".snipara/workflow/current.json" ]; then
1150
+ rlm-hook task-commit --summary "Claude Code compacted while a managed workflow state existed. Resume with rlm-hook workflow resume --include-session-context and continue from .snipara/workflow/current.json." --category workflow-compaction --outcome partial || true
1151
+ else
1152
+ rlm-hook task-commit --summary "Claude Code compacted local session context" --outcome partial || true
1153
+ fi
1077
1154
  fi
1078
1155
  `;
1079
1156
  }
@@ -1149,7 +1226,7 @@ function getClientGuidance(client) {
1149
1226
  if (client === "codex") {
1150
1227
  return [
1151
1228
  "Codex should use the hosted MCP surface plus AGENTS.md as the canonical project instruction file.",
1152
- "The generated .snipara/templates/codex-config.toml can be merged into ~/.codex/config.toml."
1229
+ "The generated .snipara/templates/codex-config.toml is a merge-ready snippet for ~/.codex/config.toml if your Codex app does not read project .codex/config.toml."
1153
1230
  ];
1154
1231
  }
1155
1232
  if (client === "chatgpt") {
@@ -1222,6 +1299,21 @@ function getCompanionCommands(answers) {
1222
1299
  command: 'rlm-hook plan --query "Implement webhook retry hardening" --max-tokens 3000',
1223
1300
  purpose: "Produce a structured execution plan against indexed project knowledge."
1224
1301
  },
1302
+ {
1303
+ title: "Start a managed workflow from the visible plan",
1304
+ command: 'rlm-hook workflow start --goal "Implement webhook retry hardening" --plan-file ./plan.md',
1305
+ purpose: "Record the LLM-visible plan in .snipara/workflow/current.json so phase state survives compaction."
1306
+ },
1307
+ {
1308
+ title: "Start the current workflow phase",
1309
+ command: "rlm-hook workflow phase-start context",
1310
+ purpose: "Mark a phase active and print the required Snipara context gate for the agent."
1311
+ },
1312
+ {
1313
+ title: "Analyze code impact before a risky change",
1314
+ command: 'rlm-hook code impact --changed-files src/api/webhooks.ts tests/webhooks.test.ts --diff-summary "webhook retry hardening"',
1315
+ purpose: "Use paid Context code impact analysis to surface likely tests, docs, routes, config, and risks."
1316
+ },
1225
1317
  {
1226
1318
  title: "Upload or refresh a doc",
1227
1319
  command: "rlm-hook upload --path docs/local-playbook.md --file ./docs/local-playbook.md",
@@ -1247,6 +1339,16 @@ function getCompanionCommands(answers) {
1247
1339
  title: "Commit durable task memory",
1248
1340
  command: 'rlm-hook task-commit --summary "Hardened webhook retries and updated tests" --category engineering --outcome completed --files src/api/webhooks.ts tests/webhooks.test.ts',
1249
1341
  purpose: "Persist reusable outcomes back into hosted memory after the work is done."
1342
+ },
1343
+ {
1344
+ title: "Commit a managed workflow phase",
1345
+ command: 'rlm-hook workflow phase-commit context --summary "Loaded context and mapped impacted files" --files src/api/webhooks.ts tests/webhooks.test.ts',
1346
+ purpose: "Persist this phase through hosted memory and advance the local managed workflow state."
1347
+ },
1348
+ {
1349
+ title: "Commit the final managed workflow result",
1350
+ command: 'rlm-hook final-commit --summary "Shipped webhook retry hardening and tests" --files src/api/webhooks.ts tests/webhooks.test.ts',
1351
+ purpose: "Persist the final workflow outcome and close the managed workflow after verification."
1250
1352
  }
1251
1353
  ];
1252
1354
  }
@@ -1259,7 +1361,7 @@ async function createCompanionPack(answers) {
1259
1361
  fs.mkdirSync(packDir, { recursive: true });
1260
1362
  const readme = `# Snipara Companion Pack
1261
1363
 
1262
- This project uses Snipara as a hosted-first context and memory layer with an optional local companion CLI.
1364
+ This project uses Snipara as a hosted-first context and memory layer with local companion workflow helpers.
1263
1365
 
1264
1366
  ## Install Profile
1265
1367
 
@@ -1325,6 +1427,7 @@ ${item.purpose}`
1325
1427
 
1326
1428
  - Context first: use hosted queries, shared-context, and document loads for source truth.
1327
1429
  - Memory second: use session bootstrap and task commit for durable decisions, learnings, and preferences.
1430
+ - Managed workflow when a visible plan exists: \`workflow start\`, \`phase-start\`, \`phase-commit\`, \`workflow resume\`, and \`final-commit\` keep phase state durable across compaction.
1328
1431
  - Durable memory, policy, and orchestration stay hosted in Snipara.
1329
1432
  - The local companion is optional tooling for capture, bootstrap, upload, exploration, doctor, repair, and explicit task summaries.
1330
1433
  `;
@@ -1449,7 +1552,7 @@ async function runValidation(answers, includeHostedCheck) {
1449
1552
  detail: answers.installHooks && answers.aiClient === "claude-code" ? detection.files.claudeDir ? "~/.claude detected" : "~/.claude missing" : "Hooks not requested"
1450
1553
  });
1451
1554
  if (includeHostedCheck && answers.apiKeyType !== "none" && !answers.apiKey.includes("YOUR_")) {
1452
- checks.push(await testConnection(answers));
1555
+ checks.push(...await testHostedConnection(answers));
1453
1556
  } else if (includeHostedCheck) {
1454
1557
  checks.push({
1455
1558
  name: "hosted_connection",
@@ -1488,47 +1591,118 @@ function printDoctorReport(detection, report) {
1488
1591
  }
1489
1592
  console.log("");
1490
1593
  }
1491
- async function testConnection(answers) {
1492
- const spinner = ora("Connecting to Snipara API...").start();
1594
+ async function testHostedConnection(answers) {
1595
+ const spinner = ora("Connecting to Snipara Hosted MCP...").start();
1493
1596
  try {
1494
- const response = await fetch(`${API_URL}/mcp/${answers.projectSlug}`, {
1495
- method: "POST",
1496
- headers: {
1497
- "Content-Type": "application/json",
1498
- "X-API-Key": answers.apiKey
1499
- },
1500
- body: JSON.stringify({
1501
- jsonrpc: "2.0",
1502
- id: 1,
1503
- method: "tools/list",
1504
- params: {}
1505
- })
1506
- });
1507
- if (response.ok) {
1508
- const data = await response.json();
1509
- const toolCount = data.result?.tools?.length || 0;
1510
- spinner.succeed(`Connected! ${toolCount} MCP tools available.`);
1511
- return {
1597
+ const toolsResponse = await callHostedMcp(answers, "tools/list", {});
1598
+ if (!toolsResponse.ok) {
1599
+ spinner.warn(`Hosted MCP returned HTTP ${toolsResponse.status}`);
1600
+ return [
1601
+ {
1602
+ name: "hosted_connection",
1603
+ status: toolsResponse.status === 401 || toolsResponse.status === 403 ? "fail" : "warn",
1604
+ detail: describeHostedHttpFailure(toolsResponse.status, toolsResponse.bodyText)
1605
+ }
1606
+ ];
1607
+ }
1608
+ if (toolsResponse.data.error) {
1609
+ spinner.warn("Hosted MCP tools/list returned a JSON-RPC error");
1610
+ return [
1611
+ {
1612
+ name: "hosted_connection",
1613
+ status: "warn",
1614
+ detail: describeJsonRpcError(toolsResponse.data)
1615
+ }
1616
+ ];
1617
+ }
1618
+ const toolCount = toolsResponse.data.result?.tools?.length || 0;
1619
+ spinner.succeed(`Connected! ${toolCount} MCP tools available.`);
1620
+ const checks = [
1621
+ {
1512
1622
  name: "hosted_connection",
1513
1623
  status: "pass",
1514
- detail: `Connected successfully (${toolCount} tools reported)`
1515
- };
1624
+ detail: `tools/list succeeded (${toolCount} tools reported)`
1625
+ }
1626
+ ];
1627
+ const settingsResponse = await callHostedMcp(answers, "tools/call", {
1628
+ name: "rlm_settings",
1629
+ arguments: {}
1630
+ });
1631
+ if (settingsResponse.ok && !settingsResponse.data.error) {
1632
+ checks.push({
1633
+ name: "hosted_settings",
1634
+ status: "pass",
1635
+ detail: "rlm_settings tool call succeeded"
1636
+ });
1637
+ } else {
1638
+ checks.push({
1639
+ name: "hosted_settings",
1640
+ status: "warn",
1641
+ detail: settingsResponse.ok ? describeJsonRpcError(settingsResponse.data) : describeHostedHttpFailure(settingsResponse.status, settingsResponse.bodyText)
1642
+ });
1516
1643
  }
1517
- const errorText = await response.text();
1518
- spinner.warn(`Connection returned ${response.status}`);
1519
- return {
1520
- name: "hosted_connection",
1521
- status: "warn",
1522
- detail: `HTTP ${response.status}: ${errorText.slice(0, 120)}`
1523
- };
1644
+ return checks;
1524
1645
  } catch (error) {
1525
1646
  spinner.warn(`Could not connect: ${error}`);
1526
- return {
1527
- name: "hosted_connection",
1528
- status: "warn",
1529
- detail: "Could not reach Snipara API from this machine"
1530
- };
1647
+ return [
1648
+ {
1649
+ name: "hosted_connection",
1650
+ status: "warn",
1651
+ detail: "Could not reach Snipara Hosted MCP from this machine"
1652
+ }
1653
+ ];
1654
+ }
1655
+ }
1656
+ async function callHostedMcp(answers, method, params) {
1657
+ const response = await fetch(`${API_URL}/mcp/${answers.projectSlug}`, {
1658
+ method: "POST",
1659
+ headers: {
1660
+ "Content-Type": "application/json",
1661
+ "X-API-Key": answers.apiKey
1662
+ },
1663
+ body: JSON.stringify({
1664
+ jsonrpc: "2.0",
1665
+ id: 1,
1666
+ method,
1667
+ params
1668
+ })
1669
+ });
1670
+ const bodyText = await response.text();
1671
+ let data = {};
1672
+ try {
1673
+ data = bodyText ? JSON.parse(bodyText) : {};
1674
+ } catch {
1675
+ data = {};
1676
+ }
1677
+ return {
1678
+ ok: response.ok,
1679
+ status: response.status,
1680
+ data,
1681
+ bodyText
1682
+ };
1683
+ }
1684
+ function describeHostedHttpFailure(status, bodyText) {
1685
+ const detail = bodyText.trim().slice(0, 120);
1686
+ if (status === 401 || status === 403) {
1687
+ return `Auth failed (${status}). Check the project/team API key and project slug.`;
1688
+ }
1689
+ if (status === 404) {
1690
+ return "Endpoint or project slug was not found. Check the hosted MCP URL.";
1531
1691
  }
1692
+ if (status === 429) {
1693
+ return "Rate limit reached. Retry later or check plan limits.";
1694
+ }
1695
+ if (status >= 500) {
1696
+ return `Hosted MCP server error (${status}). Retry later or check Snipara status.`;
1697
+ }
1698
+ return detail ? `HTTP ${status}: ${detail}` : `HTTP ${status}`;
1699
+ }
1700
+ function describeJsonRpcError(data) {
1701
+ if (!data.error) {
1702
+ return "Unexpected hosted MCP response";
1703
+ }
1704
+ const code = data.error.code !== void 0 ? ` ${data.error.code}` : "";
1705
+ return `JSON-RPC error${code}: ${data.error.message || "unknown error"}`;
1532
1706
  }
1533
1707
  function printSuccess(answers, report) {
1534
1708
  console.log(
@@ -1572,6 +1746,11 @@ function printSuccess(answers, report) {
1572
1746
  nextStep += 1;
1573
1747
  };
1574
1748
  printNextStep("Restart your AI client if you created or changed hosted MCP config.");
1749
+ if (answers.aiClient === "codex") {
1750
+ printNextStep(
1751
+ "For Codex, merge .snipara/templates/codex-config.toml into ~/.codex/config.toml if the app does not read project .codex/config.toml."
1752
+ );
1753
+ }
1575
1754
  printNextStep("Open .snipara/companion/README.md for local commands and repair flow.");
1576
1755
  printNextStep("Run create-snipara doctor anytime you want a fresh local validation report.");
1577
1756
  if (answers.installCompanion) {
@@ -1697,7 +1876,7 @@ function resolveMaintenanceProfile(detection, explicitProfile) {
1697
1876
  if (detection.existingConfig.hasSniparaServer) {
1698
1877
  return "hosted-only";
1699
1878
  }
1700
- return detection.tooling.runtimeCli ? "runtime-only" : "hosted-only";
1879
+ return "hosted-companion";
1701
1880
  }
1702
1881
  function readApiKeyFromEnvFiles() {
1703
1882
  const candidates = [".env.local", ".env"];
@@ -1705,7 +1884,7 @@ function readApiKeyFromEnvFiles() {
1705
1884
  const filePath = path.join(process.cwd(), file);
1706
1885
  if (!fs.existsSync(filePath)) continue;
1707
1886
  const content = fs.readFileSync(filePath, "utf-8");
1708
- const match = content.match(/^SNIPARA_API_KEY=(.+)$/m);
1887
+ const match = content.match(/^(?:SNIPARA_API_KEY|RLM_API_KEY)=(.+)$/m);
1709
1888
  if (match?.[1]) {
1710
1889
  return match[1].trim();
1711
1890
  }
@@ -1715,7 +1894,8 @@ function readApiKeyFromEnvFiles() {
1715
1894
  function detectProjectSlug() {
1716
1895
  try {
1717
1896
  const remote = execSync("git remote get-url origin", {
1718
- encoding: "utf-8"
1897
+ encoding: "utf-8",
1898
+ stdio: ["ignore", "pipe", "ignore"]
1719
1899
  }).trim();
1720
1900
  const match = remote.match(/[/:]([^/]+?)(?:\.git)?$/);
1721
1901
  if (match) {
@@ -1728,7 +1908,8 @@ function detectProjectSlug() {
1728
1908
  function detectGitRepository() {
1729
1909
  try {
1730
1910
  const remoteUrl = execSync("git remote get-url origin", {
1731
- encoding: "utf-8"
1911
+ encoding: "utf-8",
1912
+ stdio: ["ignore", "pipe", "ignore"]
1732
1913
  }).trim();
1733
1914
  const match = remoteUrl.match(/github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?$/) || remoteUrl.match(/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
1734
1915
  if (!match) {
@@ -1751,7 +1932,8 @@ function detectGitRepository() {
1751
1932
  function detectGitBranch() {
1752
1933
  try {
1753
1934
  const branch = execSync("git branch --show-current", {
1754
- encoding: "utf-8"
1935
+ encoding: "utf-8",
1936
+ stdio: ["ignore", "pipe", "ignore"]
1755
1937
  }).trim();
1756
1938
  if (branch) {
1757
1939
  return branch;
@@ -1760,7 +1942,8 @@ function detectGitBranch() {
1760
1942
  }
1761
1943
  try {
1762
1944
  const branch = execSync("git rev-parse --abbrev-ref HEAD", {
1763
- encoding: "utf-8"
1945
+ encoding: "utf-8",
1946
+ stdio: ["ignore", "pipe", "ignore"]
1764
1947
  }).trim();
1765
1948
  return branch && branch !== "HEAD" ? branch : "main";
1766
1949
  } catch {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-snipara",
3
- "version": "1.3.8",
4
- "description": "Onboard Snipara Hosted MCP Context + Memory with auth plus optional local helper and runtime workflows",
3
+ "version": "1.3.11",
4
+ "description": "Onboard Snipara Hosted MCP Context + Memory with auth, companion workflows, and optional runtime",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
7
7
  "bin": {
@@ -11,6 +11,8 @@
11
11
  "build": "tsup src/index.ts --format esm --dts --clean",
12
12
  "dev": "tsup src/index.ts --format esm --watch",
13
13
  "prepublishOnly": "npm run build",
14
+ "pretest": "pnpm build",
15
+ "test": "node --test test/*.test.cjs",
14
16
  "pack:smoke": "bash ../../scripts/smoke-node-package.sh packages/create-snipara create-snipara create-snipara --help",
15
17
  "type-check": "tsc --noEmit",
16
18
  "lint": "eslint src --ext .ts"