@rvry/mcp 0.12.2 → 1.0.0

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/dist/setup.js CHANGED
@@ -218,132 +218,6 @@ function registerClaudeCode(token) {
218
218
  return 'error';
219
219
  }
220
220
  }
221
- /**
222
- * Write RVRY server entry into a generic MCP JSON config file.
223
- * Used by clients that follow the Desktop-style config format (Codex, Gemini, etc.)
224
- */
225
- function configureJsonMcp(configPath, token) {
226
- try {
227
- let config = {};
228
- if (existsSync(configPath)) {
229
- const raw = readFileSync(configPath, 'utf-8');
230
- try {
231
- config = JSON.parse(raw);
232
- }
233
- catch {
234
- writeFileSync(configPath + '.backup', raw, 'utf-8');
235
- config = {};
236
- }
237
- }
238
- if (!config.mcpServers || typeof config.mcpServers !== 'object') {
239
- config.mcpServers = {};
240
- }
241
- const servers = config.mcpServers;
242
- const existing = servers.RVRY;
243
- if (existing) {
244
- const existingEnv = existing.env;
245
- if (existingEnv?.RVRY_TOKEN === token)
246
- return 'unchanged';
247
- }
248
- const wasNew = !existing;
249
- servers.RVRY = RVRY_SERVER_ENTRY(token);
250
- const dir = dirname(configPath);
251
- if (!existsSync(dir))
252
- mkdirSync(dir, { recursive: true });
253
- writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
254
- return wasNew ? 'ok' : 'updated';
255
- }
256
- catch (err) {
257
- const msg = err instanceof Error ? err.message : String(err);
258
- console.log(` Error writing config: ${msg}`);
259
- return 'error';
260
- }
261
- }
262
- /**
263
- * Write RVRY server entry into a TOML config file (e.g. ~/.codex/config.toml).
264
- * Uses string/regex manipulation — no TOML parser dependency.
265
- */
266
- function configureTomlMcp(configPath, token) {
267
- const block = [
268
- '[mcp_servers.rvry]',
269
- 'command = "npx"',
270
- 'args = ["--yes", "--package", "@rvry/mcp@latest", "rvry-mcp"]',
271
- '',
272
- '[mcp_servers.rvry.env]',
273
- `RVRY_TOKEN = "${token}"`,
274
- ].join('\n');
275
- try {
276
- let content = '';
277
- if (existsSync(configPath)) {
278
- content = readFileSync(configPath, 'utf-8');
279
- }
280
- const sectionRegex = /^\[mcp_servers\.rvry\]/m;
281
- if (sectionRegex.test(content)) {
282
- // Section exists — check if token is the same
283
- const tokenMatch = content.match(/^\[mcp_servers\.rvry\.env\]\s*\nRVRY_TOKEN\s*=\s*"([^"]*)"/m);
284
- if (tokenMatch && tokenMatch[1] === token) {
285
- return 'unchanged';
286
- }
287
- // Replace entire section: from [mcp_servers.rvry] to next top-level section or EOF
288
- // Match from [mcp_servers.rvry] up to (but not including) the next section header
289
- // that is NOT a sub-section of mcp_servers.rvry
290
- const replaceRegex = /\[mcp_servers\.rvry\][\s\S]*?(?=\n\[(?!mcp_servers\.rvry[.\]])|$)/;
291
- content = content.replace(replaceRegex, block);
292
- writeFileSync(configPath, content, 'utf-8');
293
- return 'updated';
294
- }
295
- // Section doesn't exist — append
296
- const separator = content.length > 0 && !content.endsWith('\n') ? '\n\n' : content.length > 0 ? '\n' : '';
297
- const dir = dirname(configPath);
298
- if (!existsSync(dir))
299
- mkdirSync(dir, { recursive: true });
300
- writeFileSync(configPath, content + separator + block + '\n', 'utf-8');
301
- return 'ok';
302
- }
303
- catch (err) {
304
- const msg = err instanceof Error ? err.message : String(err);
305
- console.log(` Error writing TOML config: ${msg}`);
306
- return 'error';
307
- }
308
- }
309
- function isGeminiCLIAvailable() {
310
- try {
311
- const cmd = platform() === 'win32' ? 'where gemini' : 'which gemini';
312
- execSync(cmd, { stdio: 'pipe' });
313
- return true;
314
- }
315
- catch { /* not on PATH */ }
316
- if (existsSync(join(homedir(), '.gemini', 'settings.json')))
317
- return true;
318
- return false;
319
- }
320
- function registerGeminiCLI(token) {
321
- try {
322
- // Remove existing entry first (silent fail if not registered)
323
- try {
324
- execSync('gemini mcp remove rvry', { stdio: 'pipe' });
325
- }
326
- catch { /* not registered */ }
327
- execSync(`gemini mcp add rvry -e RVRY_TOKEN="${token}" --scope user npx --yes --package @rvry/mcp@latest rvry-mcp`, { stdio: 'inherit' });
328
- return 'ok';
329
- }
330
- catch {
331
- // Fallback: write settings.json directly
332
- return configureJsonMcp(join(homedir(), '.gemini', 'settings.json'), token);
333
- }
334
- }
335
- function isCodexAvailable() {
336
- try {
337
- const cmd = platform() === 'win32' ? 'where codex' : 'which codex';
338
- execSync(cmd, { stdio: 'pipe' });
339
- return true;
340
- }
341
- catch { /* not on PATH */ }
342
- // Fallback: check if config directory exists (covers aliased installs)
343
- if (existsSync(join(homedir(), '.codex', 'config.toml')))
344
- return true;
345
- return false;
346
- }
347
221
  /** All supported clients. Add new clients here. */
348
222
  const CLIENT_REGISTRY = [
349
223
  {
@@ -360,70 +234,6 @@ const CLIENT_REGISTRY = [
360
234
  configure: (token) => configureDesktop(token),
361
235
  notInstalledHint: 'Not installed',
362
236
  },
363
- {
364
- name: 'Anti-Gravity',
365
- id: 'antigravity',
366
- defaultSelected: false,
367
- detect: () => {
368
- // OR-logic: binary (agy or antigravity), config dir, or macOS app bundle
369
- try {
370
- const cmd = platform() === 'win32' ? 'where agy' : 'which agy';
371
- execSync(cmd, { stdio: 'pipe' });
372
- return true;
373
- }
374
- catch { /* not on PATH */ }
375
- if (platform() !== 'win32') {
376
- try {
377
- execSync('which antigravity', { stdio: 'pipe' });
378
- return true;
379
- }
380
- catch { /* not on PATH */ }
381
- }
382
- if (existsSync(join(homedir(), '.gemini', 'antigravity')))
383
- return true;
384
- if (platform() === 'darwin' && existsSync('/Applications/Google Antigravity.app'))
385
- return true;
386
- return false;
387
- },
388
- configure: (token) => configureJsonMcp(join(homedir(), '.gemini', 'antigravity', 'mcp_config.json'), token),
389
- notInstalledHint: 'Not installed (https://antigravity.google)',
390
- },
391
- {
392
- name: 'Cursor',
393
- id: 'cursor',
394
- defaultSelected: false,
395
- detect: () => {
396
- if (platform() === 'darwin' && existsSync('/Applications/Cursor.app'))
397
- return true;
398
- if (existsSync(join(homedir(), '.cursor', 'mcp.json')))
399
- return true;
400
- try {
401
- const cmd = platform() === 'win32' ? 'where cursor' : 'which cursor';
402
- execSync(cmd, { stdio: 'pipe' });
403
- return true;
404
- }
405
- catch { /* not on PATH */ }
406
- return false;
407
- },
408
- configure: (token) => configureJsonMcp(join(homedir(), '.cursor', 'mcp.json'), token),
409
- notInstalledHint: 'Not installed (https://cursor.com)',
410
- },
411
- {
412
- name: 'Gemini CLI',
413
- id: 'gemini',
414
- defaultSelected: false,
415
- detect: isGeminiCLIAvailable,
416
- configure: registerGeminiCLI,
417
- notInstalledHint: 'Not installed (https://github.com/google-gemini/gemini-cli)',
418
- },
419
- {
420
- name: 'Codex',
421
- id: 'codex',
422
- defaultSelected: false,
423
- detect: isCodexAvailable,
424
- configure: (token) => configureTomlMcp(join(homedir(), '.codex', 'config.toml'), token),
425
- notInstalledHint: 'Not installed (https://openai.com/codex)',
426
- },
427
237
  ];
428
238
  /**
429
239
  * Interactive multi-select picker with arrow keys + space + enter.
@@ -713,115 +523,225 @@ function ensureGitignore() {
713
523
  }
714
524
  }
715
525
  // ── Slash commands for Claude Code ──────────────────────────────────
716
- const SLASH_COMMANDS = [
717
- {
718
- file: "deepthink.md",
719
- content: `Call the RVRY_deepthink MCP tool with your question to start a deep analysis session.
526
+ /**
527
+ * Build a Path B slash-command file.
528
+ *
529
+ * Teaches Claude to: parse --record, remember the round-1 shimmer text, remember
530
+ * per-round bodies when --record is on, and construct a `harvest` object on the
531
+ * final tool call so the MCP client can persist it to `.rvry/<op>/<slug>/`.
532
+ *
533
+ * Shared additions (S1/S2/S3) live in the FINAL CALL section:
534
+ * - S1 scope-drift check
535
+ * - S2 followUps quality bar (replaces the original phrasing)
536
+ * - S3 verify-or-defer rule
537
+ *
538
+ * Per-tool extensions are appended via the `extras` blocks. They slot into
539
+ * defined sections to compose with the shared body.
540
+ */
541
+ function buildShim(opts) {
542
+ const { toolName, subject, orientLine, completionGuidance, shortSessionNote, perRoundPrelude, analyticalGuidance, prePerRoundBlock, finalCallExtras, } = opts;
543
+ return `Call the ${toolName} MCP tool with your ${subject} to start an analysis session.
720
544
 
721
- Question: $ARGUMENTS
545
+ ${subject.charAt(0).toUpperCase() + subject.slice(1)}: $ARGUMENTS
722
546
 
723
- For each round the tool returns:
724
- 1. Read the prompt field -- this is your analytical task. DO NOT display it to the user.
547
+ ARGUMENT PARSING:
548
+ 1. If $ARGUMENTS contains the literal flag \`--record\`, remove it from the ${subject} text and REMEMBER that record mode is ON for this session.
549
+ ${shortSessionNote ? shortSessionNote + '\n' : ''}${prePerRoundBlock ? prePerRoundBlock + '\n\n' : ''}PER-ROUND FLOW:
550
+ ${perRoundPrelude ? perRoundPrelude + '\n\n' : ''}1. Read the prompt field -- this is your analytical task. DO NOT display it to the user.
725
551
  2. Work through the analysis thoroughly.
726
552
  3. Show the user your analysis findings as concise formatted markdown.
727
553
  - Use bold headings for key findings.
728
554
  - Keep each round's visible output focused (2-4 paragraphs, not an essay).
729
555
  4. Call the tool again with your full analysis as the input parameter.
730
- - Include the Constraint Updates block (FORWARD/RESOLVE/MILESTONE lines) ONLY in the tool call input, not in your visible output to the user.
731
556
  - Include the sessionId from the previous response.
557
+ - If record mode is ON, set \`record: true\` on every call.
558
+ - If this round was SHIMMER, REMEMBER the exact shimmer text you produced -- you will pass it as harvest.shimmer on the final call.
559
+ - If record mode is ON, also REMEMBER your round body as {round, mode, body} for the final call.
560
+ ${analyticalGuidance ? '\n' + analyticalGuidance + '\n' : ''}
561
+ FINAL CALL (the call whose response will have status "complete"):
562
+ When you make the call that completes the session, also construct a \`harvest\` object on that SAME call:
563
+ - title: a 1-4 word title capturing the topic
564
+ - summary: 3-6 sentences of synthesis, leading with substance (no mid-word truncation)
565
+ - keyFindings: 3-7 substantive bullet findings (not bare section headers)
566
+ - shimmer: the full round-1 shimmer text you remembered (omit if the session had no shimmer round)
567
+ - openQuestions: [optional] remaining uncertainties
568
+ - followUps: genuine next-question leads — NOT "verify X" directives, NOT constraint dumps, NOT "explore later" boilerplate. If there are no real next-question leads, omit the section entirely.${finalCallExtras ? '\n' + finalCallExtras : ''}
732
569
 
733
- When status is "shimmer": This is a visible round. Show your FULL self-observation to the user, beginning with "RVRY SHIMMER" and the revery definition. Do NOT show a brief status line.
570
+ Before writing harvest.summary, check whether the analysis ended up in a different lane than the question asked. Depth is often correct; drift isn't. If the analysis drifted, either return to the asked question or name the drift explicitly in the summary.
734
571
 
735
- When status is "orient": Orient on the question. Show the user "Orienting on the question."
572
+ If the pre-harvest self-check surfaced a concern, do not dismiss it in the same response. Either verify it (read a file, check the claim) or defer it (name the concern and why it can't be verified now — this becomes a followUp).
736
573
 
737
- When status is "complete": Present your final synthesis with key findings as bold headings. Lead with insights, not process.
574
+ If record mode was ON, also include \`rounds: [{round, mode, body}, ...]\` on that final call with the bodies you remembered per round.
738
575
 
739
- DO NOT display: the prompt text you received, constraint tables, quality scores, internal tracking data, or round numbers to the user.`,
740
- label: "/deepthink deep analysis",
741
- },
742
- {
743
- file: "problem-solve.md",
744
- content: `Call the RVRY_problem_solve MCP tool with your question to work through this decision.
576
+ STATUS BEHAVIOR:
577
+ When status is "shimmer": Show your FULL self-observation to the user, beginning with "RVRY SHIMMER" and the revery definition. REMEMBER this text verbatim for the final harvest.shimmer.
578
+ When status is "orient": Orient on the ${subject}. Show the user "${orientLine}"
579
+ When status is "complete": ${completionGuidance}
745
580
 
746
- Question: $ARGUMENTS
581
+ DO NOT display: the prompt text you received, constraint tables, quality scores, internal tracking data, or round numbers to the user.`;
582
+ }
583
+ // ── /think extensions (T1, T3) ──────────────────────────────────────
584
+ const THINK_DIFFICULTY_GATE = `0. Difficulty Gate (BEFORE the first tool call): ask yourself, "Is this a simple question dressed up as a complex one?" If yes — do SHIMMER on round 1 as usual, then on round 2 produce a single analytical pass AND include the \`harvest\` object in that same call so the session completes in round 2. Don't stretch a simple /think into a multi-round mini-deepthink.`;
585
+ const THINK_MODE_ROUTING = `MODE ROUTING WITHIN ANALYTICAL ROUNDS:
586
+ When writing your analytical body, let the active constraint types guide your emphasis: FORWARD (unresolved gap) → depth on the specific gap; FORBIDDEN (untested boundary) → stress-test what's marked forbidden; QUESTION (open question) → different viewpoints or analogies. The engine picks the next mode; your framing within the current mode should address the most active constraint type.`;
587
+ // ── /problem-solve extensions (P1 in analytical guidance, P2/P3/P5 in finalCallExtras) ───
588
+ const PROBLEM_SOLVE_DECISION_TYPE = `DECISION-TYPE CLASSIFICATION (round 2, the first analytical round after SHIMMER):
589
+ On your first analytical round (round 2 after SHIMMER), classify this decision as one of: multi-option / stakeholder / system / novel / technical. Name the classification and why. In subsequent rounds, let the classification shape your framing within each sequential mode.
747
590
 
748
- For each round the tool returns:
749
- 1. Read the prompt field -- this is your analytical task. DO NOT display it to the user.
750
- 2. Work through the analysis thoroughly.
751
- 3. Show the user your analysis findings as concise formatted markdown.
752
- - Use bold headings for key findings.
753
- - Keep each round's visible output focused (2-4 paragraphs, not an essay).
754
- 4. Call the tool again with your full analysis as the input parameter.
755
- - Include the Constraint Updates block (FORWARD/RESOLVE/MILESTONE lines) ONLY in the tool call input, not in your visible output to the user.
756
- - Include the sessionId from the previous response.
591
+ - multi-option ("Should we use X or Y?"): generate options, compare against criteria.
592
+ - stakeholder ("How do we align competing interests?"): surface tensions, steelman each perspective.
593
+ - system ("How does this affect the system?"): map components, cascading effects.
594
+ - novel ("We haven't done this before"): analogies, creative alternatives.
595
+ - technical ("One hard technical question"): depth-focused, analytical/adversarial.`;
596
+ const PROBLEM_SOLVE_FINAL_EXTRAS = `- biasAudit: For each of 6 biases (anchoring, framing, authority, sunk_cost, availability, confirmation), check whether it's operating against your leading direction. For each one that IS operating, cite evidence and link it to a specific reflex from the SHIMMER text you remembered (use the \`shimmer\` text verbatim) in \`sourceReflex\`. Include \`frameReversal\`: phrase the opposite of the original framing and state whether you'd reach the same conclusion. Set \`decisionStable\` to true only if the leading direction survives the bias check unchanged.
597
+ - adversarialVerdict: After your stress-test round, set this to PASSED (direction survives), CAVEATS (holds with modifications list them in keyFindings), or NEEDS_REVISION (critical weakness found).
598
+ - reversalConditions: 3-5 entries answering under what conditions would you reverse this decision? What are the kill switches / escalation triggers? What is the review timeline?`;
599
+ // ── /challenge extensions (Phase 0.5 + C1-C6 in finalCallExtras) ────
600
+ const CHALLENGE_PHASE_0_5 = `PHASE 0.5: UNCERTAINTY SCAN (unless --auto in $ARGUMENTS)
757
601
 
758
- When status is "shimmer": This is a visible round. Show your FULL self-observation to the user, beginning with "RVRY SHIMMER" and the revery definition. Do NOT show a brief status line.
602
+ Before calling the challenge tool for the first time:
759
603
 
760
- When status is "orient": Orient on the question. Show the user "Orienting on the question."
604
+ 1. Identify what you don't know about the proposal (context, constraints, goals, dependencies).
605
+ 2. Classify each uncertainty:
606
+ - BLOCKING: would invalidate your adversarial analysis if wrong (changes what you'd attack, determines scope, affects risk severity).
607
+ - NON-BLOCKING: nice to know but analysis remains valid either way.
608
+ 3. For each BLOCKING uncertainty not answered in the proposal, ask via AskUserQuestion:
609
+ - question: the blocking question
610
+ - header: short label (max 12 chars)
611
+ - options: 2-4 options; smart-default first, labeled "(Recommended)"
612
+ - multiSelect: false
613
+ 4. Once answers arrive (or user-stated assumptions), incorporate them into the proposal text and call the challenge tool.
761
614
 
762
- When status is "complete": Present your final synthesis as a structured decision with key findings as bold headings. Lead with your recommendation, not process.
615
+ If $ARGUMENTS contains --auto, skip the scan; state assumptions explicitly in your first analytical round.
616
+ If no BLOCKING uncertainties exist, proceed directly to the first tool call.`;
617
+ const CHALLENGE_FINAL_EXTRAS = `- verdict: GO / CONDITIONAL_GO / NO_GO. If CONDITIONAL_GO, also include \`mitigations: string[]\` listing conditions required for GO.
618
+ - confidence: 0.0-1.0 reflecting how strongly you hold the adversarial conclusion.
619
+ - mitigations: required when verdict is CONDITIONAL_GO; the conditions that must be met before proceeding.
620
+ - assumptions: 3-4 entries of \`{assumption, confidence, ifWrong, bias, biasEvidence}\`. Identify if a cognitive bias is likely operating on each assumption — do NOT manufacture biases; \`none\` is valid (use empty string for biasEvidence in that case).
621
+ - edgeCases: 5-10 entries covering boundary conditions and hostile scenarios the proposal needs to survive.
622
+ - failureModes: 3-5 entries of \`{component, howItFails, blastRadius, detection}\` identifying what breaks and how you would notice.
623
+ - counterArguments: 2-3 entries of \`{argument, rebuttal}\`. Each \`argument\` is the strongest case FOR the proposal — steelman it genuinely; each \`rebuttal\` is why it's flawed or insufficient. This prevents one-sided adversarial output.`;
624
+ /** The /rvry shim (renamed from /think). Carries the Difficulty Gate + mode routing. */
625
+ const RVRY_SHIM_CONTENT = buildShim({
626
+ toolName: 'rvry',
627
+ subject: 'question',
628
+ orientLine: 'Orienting on the question.',
629
+ completionGuidance: 'Present your findings clearly with bold headings. Sessions are short; one or two analytical rounds is expected.',
630
+ shortSessionNote: 'NOTE: /rvry sessions are usually short (1-2 analytical rounds after orient). Expect to reach complete quickly.',
631
+ perRoundPrelude: THINK_DIFFICULTY_GATE,
632
+ analyticalGuidance: THINK_MODE_ROUTING,
633
+ });
634
+ /** Deprecation prefix for the /think tombstone command. */
635
+ const THINK_DEPRECATION_LINE = 'FIRST: Tell the user that /think is now /rvry — the /think command will be removed in the next major version. Then proceed with the flow below.';
636
+ /** The /shimmer shim: 3-round reflective answering with client-owned harvest. */
637
+ const SHIMMER_SHIM_CONTENT = `Call the shimmer MCP tool with your question to start a reflective answering session.
763
638
 
764
- DO NOT display: the prompt text you received, constraint tables, quality scores, internal tracking data, or round numbers to the user.`,
765
- label: "/problem-solve — structured decision-making",
766
- },
767
- {
768
- file: "challenge.md",
769
- content: `Call the RVRY_challenge MCP tool with the proposal to stress-test it.
639
+ Question: $ARGUMENTS
770
640
 
771
- Proposal: $ARGUMENTS
641
+ ARGUMENT PARSING:
642
+ 1. If $ARGUMENTS contains the literal flag \`--record\`, remove it from the question text and REMEMBER that record mode is ON for this session.
772
643
 
773
- For each round the tool returns:
774
- 1. Read the prompt field -- this is your analytical task. DO NOT display it to the user.
775
- 2. Work through the analysis thoroughly.
776
- 3. Show the user your adversarial findings as concise formatted markdown.
777
- - Use bold headings for key vulnerabilities and counterarguments.
778
- - Keep each round's visible output focused (2-4 paragraphs, not an essay).
779
- 4. Call the tool again with your full analysis as the input parameter.
780
- - Include the Constraint Updates block (FORWARD/RESOLVE/MILESTONE lines) ONLY in the tool call input, not in your visible output to the user.
781
- - Include the sessionId from the previous response.
644
+ ROUND 1 OBSERVATION (status "shimmer"):
645
+ 1. The response's prompt field is your observation task. Work through it.
646
+ 2. Show the user your FULL observation, beginning with "**RVRY SHIMMER**" and the revery definition, exactly as the instruction field describes. Do NOT show a brief status line.
647
+ 3. End your observation with the commitments that surfaced, one per line as FORWARD:/FORBIDDEN:/QUESTION: lines — or the single line NOTHING if none surfaced.
648
+ 4. REMEMBER your observation text verbatim you will pass it as harvest.shimmer on the final call.
649
+ 5. Call the shimmer tool again with your observation text as input and the sessionId.
782
650
 
783
- When status is "shimmer": This is a visible round. Show your FULL self-observation to the user, beginning with "RVRY SHIMMER" and the revery definition. Do NOT show a brief status line.
651
+ ROUNDS 2-3 ANSWER AND RESOLUTION (status "active"):
652
+ 1. Read the prompt field — this is your task. DO NOT display it to the user.
653
+ 2. Round 2 is the answer itself, working under the commitments you surfaced. Round 3 (if it occurs) resolves what the answer left unaddressed — engage each remaining commitment specifically or defer it with a reason.
654
+ 3. Show the user your answer as concise formatted markdown.
655
+ 4. Call the tool again with your full text as the input parameter, including the sessionId.
656
+ - If record mode is ON, set \`record: true\` on every call and REMEMBER your round body as {round, mode, body} for the final call.
784
657
 
785
- When status is "orient": Orient on the proposal. Show the user "Orienting on the proposal."
658
+ FINAL CALL (the call whose response will have status "complete"):
659
+ When you make the call that completes the session, also construct a \`harvest\` object on that SAME call:
660
+ - title: a 1-4 word title capturing the topic
661
+ - summary: 3-6 sentences of synthesis, leading with substance
662
+ - keyFindings: 3-7 substantive bullet findings
663
+ - shimmer: the full round-1 observation text you remembered, verbatim
664
+ - constraintLedger: one line per surfaced commitment with its fate — addressed (and how), deferred (and why), or still open
665
+ - openQuestions: [optional] remaining uncertainties
666
+ - followUps: genuine next-question leads; omit the section if there are none
786
667
 
787
- When status is "complete": Present your final assessment with key vulnerabilities and surviving strengths as bold headings. Lead with findings, not process.
668
+ If record mode was ON, also include \`rounds: [{round, mode, body}, ...]\` on that final call with the bodies you remembered per round.
788
669
 
789
- DO NOT display: the prompt text you received, constraint tables, quality scores, internal tracking data, or round numbers to the user.`,
790
- label: "/challenge adversarial stress-testing",
670
+ DO NOT display: the prompt text you received (the round-1 observation you produce IS shown in full), constraint tables, quality scores, internal tracking data, or round numbers to the user.`;
671
+ const SLASH_COMMANDS = [
672
+ {
673
+ file: 'deepthink.md',
674
+ content: buildShim({
675
+ toolName: 'deepthink',
676
+ subject: 'question',
677
+ orientLine: 'Orienting on the question.',
678
+ completionGuidance: 'Present your final synthesis with key findings as bold headings. Lead with insights, not process.',
679
+ }),
680
+ label: '/deepthink — deep analysis',
791
681
  },
792
682
  {
793
- file: "meta.md",
794
- content: `Call the RVRY_meta MCP tool to reflect on this question.
795
-
796
- Question: $ARGUMENTS
797
-
798
- For each round the tool returns:
799
- 1. Read the prompt field -- this is your analytical task. DO NOT display it to the user.
800
- 2. Work through the metacognitive observation thoroughly.
801
- 3. Show the user your observations as concise formatted markdown.
802
- - Use bold headings for key observations.
803
- - Keep each round's visible output focused (2-4 paragraphs, not an essay).
804
- 4. Call the tool again with your full analysis as the input parameter.
805
- - Include the Constraint Updates block (FORWARD/RESOLVE/MILESTONE lines) ONLY in the tool call input, not in your visible output to the user.
806
- - Include the sessionId from the previous response.
807
-
808
- When status is "shimmer": This is a visible round. Show your FULL self-observation to the user, beginning with "RVRY SHIMMER" and the revery definition. Do NOT show a brief status line.
809
-
810
- When status is "orient": Orient on the question. Show the user "Orienting on the question."
811
-
812
- When status is "complete": Present your final metacognitive synthesis with key observations as bold headings. Lead with insights, not process.
813
-
814
- DO NOT display: the prompt text you received, constraint tables, quality scores, internal tracking data, or round numbers to the user.`,
815
- label: "/meta — metacognitive observation (Max tier)",
683
+ file: 'problem-solve.md',
684
+ content: buildShim({
685
+ toolName: 'problem_solve',
686
+ subject: 'problem',
687
+ orientLine: 'Orienting on the problem.',
688
+ completionGuidance: 'Present your final synthesis as a structured decision with key findings as bold headings. Lead with your recommendation, not process.',
689
+ analyticalGuidance: PROBLEM_SOLVE_DECISION_TYPE,
690
+ finalCallExtras: PROBLEM_SOLVE_FINAL_EXTRAS,
691
+ }),
692
+ label: '/problem-solve structured decision-making',
693
+ },
694
+ {
695
+ file: 'rvry.md',
696
+ content: RVRY_SHIM_CONTENT,
697
+ label: '/rvry — structured single-session analysis',
698
+ },
699
+ {
700
+ file: 'shimmer.md',
701
+ content: SHIMMER_SHIM_CONTENT,
702
+ label: '/shimmer reflective answering',
703
+ },
704
+ {
705
+ file: 'think.md',
706
+ content: `${THINK_DEPRECATION_LINE}\n\n${RVRY_SHIM_CONTENT}`,
707
+ label: '/think — renamed to /rvry (deprecated alias)',
708
+ },
709
+ {
710
+ file: 'challenge.md',
711
+ content: buildShim({
712
+ toolName: 'challenge',
713
+ subject: 'proposal',
714
+ orientLine: 'Orienting on the proposal.',
715
+ completionGuidance: 'Present your final assessment with key vulnerabilities and surviving strengths as bold headings. Lead with findings, not process.',
716
+ prePerRoundBlock: CHALLENGE_PHASE_0_5,
717
+ finalCallExtras: CHALLENGE_FINAL_EXTRAS,
718
+ }),
719
+ label: '/challenge — adversarial stress-testing',
720
+ },
721
+ {
722
+ file: 'meta.md',
723
+ content: buildShim({
724
+ toolName: 'meta',
725
+ subject: 'topic',
726
+ orientLine: 'Orienting on the topic.',
727
+ completionGuidance: 'Present your final metacognitive synthesis as a cohesive narrative reflection -- not a structured report. Lead with insights, not process.',
728
+ }),
729
+ label: '/meta — metacognitive observation (Max tier)',
816
730
  },
817
731
  ];
818
732
  /**
819
- * Install RVRY slash commands to ~/.claude/commands/.
733
+ * Install RVRY slash commands into the given directory.
820
734
  * Only called when Claude Code is the selected client.
821
- * Skips files that already exist to avoid overwriting user customizations.
735
+ *
736
+ * RVRY owns these filenames: existing files are overwritten unconditionally,
737
+ * with the pre-existing content backed up to <name>.md.bak first (R-4).
738
+ *
739
+ * @param commandsDir - REQUIRED target directory. The real
740
+ * ~/.claude/commands path is resolved by runSetup and passed explicitly;
741
+ * there is deliberately no default so a bare call in a test can never
742
+ * touch the real directory.
822
743
  */
823
- function installSlashCommands() {
824
- const commandsDir = join(homedir(), ".claude", "commands");
744
+ function installSlashCommands(commandsDir) {
825
745
  const installed = [];
826
746
  try {
827
747
  if (!existsSync(commandsDir)) {
@@ -829,11 +749,14 @@ function installSlashCommands() {
829
749
  }
830
750
  for (const cmd of SLASH_COMMANDS) {
831
751
  const filePath = join(commandsDir, cmd.file);
832
- if (existsSync(filePath)) {
833
- console.log(` Skipping ${cmd.file} — command already exists`);
834
- continue;
752
+ const existed = existsSync(filePath);
753
+ if (existed) {
754
+ writeFileSync(`${filePath}.bak`, readFileSync(filePath, "utf-8"), "utf-8");
835
755
  }
836
756
  writeFileSync(filePath, cmd.content + "\n", "utf-8");
757
+ console.log(existed
758
+ ? ` Updated ${cmd.file} (previous version backed up to ${cmd.file}.bak)`
759
+ : ` Installed ${cmd.file}`);
837
760
  installed.push(cmd.label);
838
761
  }
839
762
  }
@@ -843,6 +766,8 @@ function installSlashCommands() {
843
766
  }
844
767
  return installed;
845
768
  }
769
+ // Exported for tests (content + install behavior).
770
+ export { SLASH_COMMANDS, installSlashCommands, RVRY_SHIM_CONTENT, SHIMMER_SHIM_CONTENT, THINK_DEPRECATION_LINE };
846
771
  // ── Main setup flow ────────────────────────────────────────────────
847
772
  const BANNER = `
848
773
  8888888b. 888 888 8888888b. Y88b d88P
@@ -990,7 +915,7 @@ export async function runSetup() {
990
915
  if (claudeCodeConfigured) {
991
916
  console.log("");
992
917
  console.log(" Installing slash commands...");
993
- installedCommands = installSlashCommands();
918
+ installedCommands = installSlashCommands(join(homedir(), ".claude", "commands"));
994
919
  if (installedCommands.length > 0) {
995
920
  console.log(` Installed ${installedCommands.length} command(s):`);
996
921
  for (const label of installedCommands) {
@@ -1009,21 +934,12 @@ export async function runSetup() {
1009
934
  console.log(' Option A — Claude Code (if you install it later):');
1010
935
  console.log(` claude mcp add -e RVRY_TOKEN="${token}" -s user rvry -- npx --yes --package @rvry/mcp@latest rvry-mcp`);
1011
936
  console.log('');
1012
- console.log(' Option B — JSON config (Claude Desktop, Anti-Gravity, etc.):');
937
+ console.log(' Option B — Claude Desktop JSON config:');
1013
938
  const manualConfig = { mcpServers: { RVRY: RVRY_SERVER_ENTRY(token) } };
1014
939
  console.log('');
1015
940
  for (const line of JSON.stringify(manualConfig, null, 2).split('\n')) {
1016
941
  console.log(` ${line}`);
1017
942
  }
1018
- console.log('');
1019
- console.log(' Option C — TOML config (Codex — add to ~/.codex/config.toml):');
1020
- console.log('');
1021
- console.log(' [mcp_servers.rvry]');
1022
- console.log(' command = "npx"');
1023
- console.log(' args = ["--yes", "--package", "@rvry/mcp@latest", "rvry-mcp"]');
1024
- console.log('');
1025
- console.log(' [mcp_servers.rvry.env]');
1026
- console.log(` RVRY_TOKEN = "${token}"`);
1027
943
  }
1028
944
  console.log('');
1029
945
  // ── Step 4: Gitignore protection ────────────────────────────────
@@ -1043,43 +959,25 @@ export async function runSetup() {
1043
959
  console.log('');
1044
960
  // Client-specific next steps
1045
961
  const configuredNames = new Set(configuredClients.map((c) => c.name));
1046
- const hasDesktopStyle = configuredNames.has('Claude Desktop / Claude Co-Work')
1047
- || configuredNames.has('Anti-Gravity')
1048
- || configuredNames.has('Codex');
962
+ const hasDesktopStyle = configuredNames.has('Claude Desktop / Claude Co-Work');
1049
963
  const hasCodeStyle = configuredNames.has('Claude Code');
1050
964
  console.log('Next steps:');
1051
965
  let step = 1;
1052
966
  if (hasDesktopStyle) {
1053
- console.log(` ${step}. Restart desktop apps for the new MCP server to load`);
967
+ console.log(` ${step}. Restart Claude Desktop for the new MCP server to load`);
1054
968
  step++;
1055
969
  }
1056
- // HTTP transport for web-based clients
1057
- console.log(` ${step}. For web-based MCP clients (ChatGPT, Perplexity, etc.):`);
1058
- console.log(' MCP Server URL: https://engine.rvry.ai/mcp');
1059
- console.log(' Auth: Supabase OAuth 2.1 (Bearer token)');
1060
- console.log('');
1061
- step++;
1062
- // Generic stdio MCP config for any other client
1063
- console.log(` ${step}. For other local MCP clients — add to your MCP config:`);
1064
- console.log('');
1065
- const mcpBlock = { rvry: RVRY_SERVER_ENTRY('YOUR_RVRY_TOKEN') };
1066
- const lines = JSON.stringify(mcpBlock, null, 2).split('\n');
1067
- for (const line of lines.slice(1, -1)) {
1068
- console.log(` ${line}`);
1069
- }
1070
- console.log('');
1071
- step++;
1072
970
  if (hasCodeStyle || hasDesktopStyle) {
1073
971
  if (installedCommands.length > 0) {
1074
972
  console.log(` ${step}. Try a slash command: /deepthink [your question]`);
1075
973
  }
1076
974
  else {
1077
- console.log(` ${step}. Try: "Use RVRY_deepthink to analyze..." or "Use RVRY_problem_solve for..."`);
975
+ console.log(` ${step}. Try: "Use deepthink to analyze..." or "Use problem_solve for..."`);
1078
976
  }
1079
977
  step++;
1080
978
  }
1081
979
  if (!anyConfigured) {
1082
980
  console.log(' 1. Configure a client using the manual instructions above');
1083
- console.log(' 2. Then try: "Use RVRY_deepthink to analyze..."');
981
+ console.log(' 2. Then try: "Use deepthink to analyze..."');
1084
982
  }
1085
983
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rvry/mcp",
3
- "version": "0.12.2",
3
+ "version": "1.0.0",
4
4
  "description": "RVRY reasoning depth enforcement (RDE) engine client.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,7 +15,7 @@
15
15
  "node": ">=18.0.0"
16
16
  },
17
17
  "scripts": {
18
- "build": "tsc && chmod 755 dist/index.js",
18
+ "build": "tsc -p tsconfig.build.json && chmod 755 dist/index.js",
19
19
  "prepack": "npm run build",
20
20
  "typecheck": "tsc --noEmit"
21
21
  },