@somacheck/vibecheck 0.6.10 → 0.6.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sensie LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -9,7 +9,7 @@ Your agent has your context. It does not have your gut. This is the bridge.
9
9
 
10
10
  Fair. Here is what you need, without executing anything.
11
11
 
12
- **What the `link` command does.** `npx -y @somacheck/vibecheck@0.6.10 link <CODE> --client <CLIENT>`
12
+ **What the `link` command does.** `npx -y @somacheck/vibecheck@0.6.11 link <CODE> --client <CLIENT>`
13
13
  exchanges a short-lived pairing code, generated inside the SomaCheck iOS app, for
14
14
  a bearer token. It writes that token to `~/.sensie/config.json` with mode `0600`,
15
15
  offers to configure supported agent clients found on the computer, and checks the
@@ -97,9 +97,9 @@ before the pairing code is redeemed.
97
97
  To configure or repair a client later:
98
98
 
99
99
  ```text
100
- npx -y @somacheck/vibecheck@0.6.10 setup codex
101
- npx -y @somacheck/vibecheck@0.6.10 setup claude
102
- npx -y @somacheck/vibecheck@0.6.10 doctor
100
+ npx -y @somacheck/vibecheck@0.6.11 setup codex
101
+ npx -y @somacheck/vibecheck@0.6.11 setup claude
102
+ npx -y @somacheck/vibecheck@0.6.11 doctor
103
103
  ```
104
104
 
105
105
  Manual registration remains available:
@@ -109,13 +109,13 @@ Manual registration remains available:
109
109
  "mcpServers": {
110
110
  "vibecheck": {
111
111
  "command": "npx",
112
- "args": ["-y", "@somacheck/vibecheck@0.6.10", "serve", "--client", "codex"]
112
+ "args": ["-y", "@somacheck/vibecheck@0.6.11", "serve", "--client", "codex"]
113
113
  }
114
114
  }
115
115
  }
116
116
  ```
117
117
 
118
- For Claude Code: `claude mcp add --scope user vibecheck -- npx -y @somacheck/vibecheck@0.6.10 serve --client claude`
118
+ For Claude Code: `claude mcp add --scope user vibecheck -- npx -y @somacheck/vibecheck@0.6.11 serve --client claude`
119
119
 
120
120
  The link step writes only the bearer token in `~/.sensie/config.json`. The client
121
121
  setup step asks Codex or Claude to add the pinned MCP command to that client's own
@@ -177,6 +177,74 @@ npx skills add Sensie-agents/vibecheck --skill vibecheck
177
177
  The skill expects the `@somacheck/vibecheck` MCP server to already be linked as
178
178
  `vibecheck`. Installing the skill does not copy or replace the bearer token.
179
179
 
180
+ ## Agent reflection recipes
181
+
182
+ Give your agent a consented signal when words are the bottleneck. These recipes
183
+ combine SomaCheck with a named platform so a person can privately reflect on
184
+ their own wording, interpretation, study design, or listening choice:
185
+
186
+ - [Typeform + SomaCheck](recipes/typeform-research-reflection.md)
187
+ - [Dovetail + SomaCheck](recipes/dovetail-research-reflection.md)
188
+ - [Prolific + SomaCheck](recipes/prolific-research-reflection.md)
189
+ - [Maze + SomaCheck](recipes/maze-research-reflection.md)
190
+ - [Chattermill + SomaCheck](recipes/chattermill-research-reflection.md)
191
+ - [User Interviews + SomaCheck](recipes/user-interviews-research-reflection.md)
192
+ - [Sprig + SomaCheck](recipes/sprig-research-reflection.md)
193
+ - [Great Question + SomaCheck](recipes/great-question-research-reflection.md)
194
+ - [QuestionPro + SomaCheck](recipes/questionpro-research-reflection.md)
195
+ - [Spotify manual Studio/Kit bridge + SomaCheck](recipes/spotify-listening-reflection.md)
196
+
197
+ The research recipes are researcher-private, and the Spotify recipe is
198
+ listener-private. They do not assess respondents, participants, creators, or
199
+ other listeners; export individual readings; authorize platform changes; or use
200
+ SomaCheck for quality, truthfulness, eligibility, payment, employment, ranking,
201
+ or research validity. A reading is context for the phone holder's next choice;
202
+ the person remains the authority.
203
+
204
+ List the packaged experiences without connecting to either MCP server:
205
+
206
+ ```text
207
+ npx -y @somacheck/vibecheck recipes
208
+ ```
209
+
210
+ Print one experience's prerequisites, success condition, and exact starter
211
+ prompt:
212
+
213
+ ```text
214
+ npx -y @somacheck/vibecheck recipe typeform-research-reflection-v1
215
+ npx -y @somacheck/vibecheck recipe dovetail-research-reflection-v1
216
+ npx -y @somacheck/vibecheck recipe prolific-research-reflection-v1
217
+ npx -y @somacheck/vibecheck recipe user-interviews-research-reflection-v1
218
+ ```
219
+
220
+ Print only a copyable starter prompt for the read-only direct-MCP tests:
221
+
222
+ ```text
223
+ npx -y @somacheck/vibecheck recipes --id maze-research-reflection-v1 --format prompt
224
+ npx -y @somacheck/vibecheck recipes --id chattermill-research-reflection-v1 --format prompt
225
+ npx -y @somacheck/vibecheck recipes --id user-interviews-research-reflection-v1 --format prompt
226
+ ```
227
+
228
+ These commands only print guides. They do not install, connect, or authorize a
229
+ partner MCP server, call either MCP, or change partner-platform state. In every
230
+ recipe, the phone holder is the person reflecting on their own choice.
231
+
232
+ ### Private Studio pilot packet
233
+
234
+ [`recipes/studio-somacheck-context.md`](recipes/studio-somacheck-context.md) is
235
+ a reviewed, paste-only context packet for a private Studio by Spotify Labs
236
+ research-preview pilot. It is deliberately absent from `vibecheck recipes`: it
237
+ is not a connected or live recipe, a Studio skill, a Kit plugin, or native MCP.
238
+ Review the packet and run its focused contract test before any pilot:
239
+
240
+ ```text
241
+ node --import tsx --test test/studio-context-packet.test.ts
242
+ ```
243
+
244
+ Run that command from `packages/vibecheck` after `npm install`. Do not grant
245
+ Studio access to this repository or another directory. File access remains
246
+ blocked until the capability and revocation probe in the safety review passes.
247
+
180
248
  ## Tools
181
249
 
182
250
  | Tool | What it does |
@@ -223,7 +291,9 @@ pending response without that hook (including the hosted connector, which is
223
291
  stateless per request and never arms the watch) tells the agent the exact
224
292
  `request_id` and the person's expiry, and to call `get_vibecheck_result` again
225
293
  in about 15 seconds, repeating at that cadence until the state is answered or
226
- expired.
294
+ expired without ending the turn or asking the person whether to check. A
295
+ still-pending read of the same Live Ask repeats that instruction; the read does
296
+ not invent an expiry because the result endpoint does not return one.
227
297
 
228
298
  The 45-second whole-call deadline also covers the one create request. If that
229
299
  network request is aborted after an ambiguous commit, retry the exact same
@@ -10,7 +10,7 @@
10
10
  "name": "vibecheck",
11
11
  "source": "./plugins/vibecheck",
12
12
  "description": "Let Claude offer a vibecheck and continue when the phone result arrives.",
13
- "version": "0.6.10",
13
+ "version": "0.6.11",
14
14
  "author": {
15
15
  "name": "Sensie",
16
16
  "email": "agents@joinsensie.com"
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "vibecheck",
3
3
  "description": "Let Claude offer a SomaCheck vibecheck and continue when the phone result arrives.",
4
- "version": "0.6.10",
4
+ "version": "0.6.11",
5
5
  "author": {
6
6
  "name": "Sensie",
7
7
  "email": "agents@joinsensie.com"
8
8
  },
9
- "homepage": "https://github.com/sensie-app/Somacheck/tree/main/packages/vibecheck",
10
- "repository": "https://github.com/sensie-app/Somacheck",
11
- "license": "UNLICENSED",
9
+ "homepage": "https://github.com/Sensie-agents/vibecheck",
10
+ "repository": "https://github.com/Sensie-agents/vibecheck",
11
+ "license": "MIT",
12
12
  "keywords": ["vibecheck", "somacheck", "decision-support", "mcp"]
13
13
  }
@@ -7,7 +7,7 @@
7
7
  "hooks": [
8
8
  {
9
9
  "type": "command",
10
- "command": "npx -y @somacheck/vibecheck@0.6.10 claude-hook await-result",
10
+ "command": "npx -y @somacheck/vibecheck@0.6.11 claude-hook await-result",
11
11
  "asyncRewake": true,
12
12
  "timeout": 900
13
13
  }
package/dist/cli.js CHANGED
@@ -10,6 +10,7 @@ import { awaitClaudeVibecheckResult } from "./claude-hook.js";
10
10
  import { PACKAGE_SPEC, SUPABASE_PUBLISHABLE_KEY, SUPABASE_URL } from "./constants.js";
11
11
  import { LinkPersistenceError, NonInteractiveLinkError, linkAgent } from "./link.js";
12
12
  import { checkReadiness } from "./readiness.js";
13
+ import { formatRecipeDetail, formatRecipeList, formatRecipePrompt, getRecipeExperience } from "./recipes.js";
13
14
  import { createVibecheckServer } from "./server.js";
14
15
  const api = new SupabaseAgentApi(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY);
15
16
  const runner = new LocalCommandRunner();
@@ -26,6 +27,9 @@ function usage() {
26
27
  " npx -y @somacheck/vibecheck link <CODE> [--client codex|claude|all|none]",
27
28
  " npx -y @somacheck/vibecheck setup <codex|claude|all>",
28
29
  " npx -y @somacheck/vibecheck doctor",
30
+ " npx -y @somacheck/vibecheck recipes",
31
+ " npx -y @somacheck/vibecheck recipes --id <id> --format prompt",
32
+ " npx -y @somacheck/vibecheck recipe <id>",
29
33
  ].join("\n");
30
34
  }
31
35
  function parseLinkClient(args) {
@@ -228,6 +232,33 @@ async function main() {
228
232
  if (args.length === 2 && args[0] === "claude-hook" && args[1] === "await-result") {
229
233
  return runClaudeResultHook();
230
234
  }
235
+ if (args[0] === "recipes") {
236
+ if (args.length === 1) {
237
+ output(formatRecipeList());
238
+ return 0;
239
+ }
240
+ if (args.length === 5 && args[1] === "--id" && args[3] === "--format" && args[4] === "prompt") {
241
+ const recipe = getRecipeExperience(args[2]);
242
+ if (recipe === null) {
243
+ process.stderr.write(`Unknown recipe ID.\n\n${formatRecipeList()}\n`);
244
+ return 1;
245
+ }
246
+ output(formatRecipePrompt(recipe));
247
+ return 0;
248
+ }
249
+ throw new Error(usage());
250
+ }
251
+ if (args[0] === "recipe") {
252
+ if (args.length !== 2)
253
+ throw new Error(usage());
254
+ const recipe = getRecipeExperience(args[1]);
255
+ if (recipe === null) {
256
+ process.stderr.write(`Unknown recipe ID.\n\n${formatRecipeList()}\n`);
257
+ return 1;
258
+ }
259
+ output(formatRecipeDetail(recipe));
260
+ return 0;
261
+ }
231
262
  if (args[0] === "link") {
232
263
  if (args.length < 2)
233
264
  throw new Error(usage());
package/dist/constants.js CHANGED
@@ -2,8 +2,9 @@
2
2
  export const SUPABASE_URL = "https://pbldcmniommltbdwuykk.supabase.co";
3
3
  export const SUPABASE_PUBLISHABLE_KEY = "sb_publishable_af-lUNI2FqEcb-oGy-4uxQ_cnm6kY85";
4
4
  export const PACKAGE_NAME = "@somacheck/vibecheck";
5
- export const PACKAGE_VERSION = "0.6.10";
5
+ export const PACKAGE_VERSION = "0.6.11";
6
6
  export const PACKAGE_SPEC = `${PACKAGE_NAME}@${PACKAGE_VERSION}`;
7
+ export const SOMACHECK_SETUP_URL = "https://testflight.apple.com/join/C4mAH3zz";
7
8
  export const MCP_SERVER_NAME = "vibecheck";
8
9
  export const LEGACY_HOSTED_MCP_SERVER_NAME = "somacheck";
9
10
  export const BACKEND_PROTOCOL_VERSION = 5;
package/dist/readiness.js CHANGED
@@ -3,7 +3,7 @@ import { join } from "node:path";
3
3
  import { readConfig } from "./config.js";
4
4
  import { SomaCheckCompatibilityError, SomaCheckHttpError } from "./api.js";
5
5
  import { clientDisplayName, clientRegistrationState, detectLegacyHostedRegistration, isClientInstalled, manualLegacyHostedRemoveCommand, manualRemoveCommand, manualSetupCommand, } from "./client-setup.js";
6
- import { BACKEND_PROTOCOL_VERSION, PACKAGE_VERSION } from "./constants.js";
6
+ import { BACKEND_PROTOCOL_VERSION, PACKAGE_VERSION, SOMACHECK_SETUP_URL } from "./constants.js";
7
7
  function vibecheckRegistrationCount(value) {
8
8
  if (!value || typeof value !== "object")
9
9
  return 0;
@@ -32,7 +32,7 @@ export async function checkReadiness(dependencies) {
32
32
  }
33
33
  catch {
34
34
  dependencies.output("✗ This computer is not linked to SomaCheck.");
35
- dependencies.output(" Install SomaCheck from the current TestFlight invitation: https://testflight.apple.com/join/C4mAH3zz");
35
+ dependencies.output(` Install SomaCheck from the current TestFlight invitation: ${SOMACHECK_SETUP_URL}`);
36
36
  dependencies.output(" Then open SomaCheck → Settings → Agent → Connect your agent and run the command shown there.");
37
37
  return {
38
38
  linked: false,
@@ -0,0 +1,191 @@
1
+ export const RECIPE_CATALOG = [
2
+ {
3
+ id: "typeform-research-reflection-v1",
4
+ title: "Typeform + SomaCheck Research Reflection",
5
+ guidePath: "recipes/typeform-research-reflection.md",
6
+ purpose: "Privately reflect on the researcher's own Typeform wording, structure, or launch readiness.",
7
+ phoneHolder: "The phone holder is the researcher reflecting on their own work, never a respondent or employee.",
8
+ prerequisites: [
9
+ "SomaCheck is ready on the researcher's iPhone and the vibecheck MCP is linked to that researcher's agent client.",
10
+ "The researcher has separately connected and authorized Typeform MCP for the correct account and data region; this command does not connect or authorize it.",
11
+ "The researcher can name one researcher-authored form, draft, or aggregate analytics view that does not require respondent rows, contacts, identifiers, or free-text answers.",
12
+ ],
13
+ starterPrompt: "Review the researcher-authored structure of my Typeform [FORM NAME OR ID] using only form structure, logic, and aggregate analytics—never respondent-level rows, contacts, identifiers, or free-text answers. Then help me privately reflect on my own wording or readiness with one optional SomaCheck vibecheck. If you propose a vibecheck, show me the exact first-person proposition and wait for my acceptance. Treat Aligned or Unaligned plus confidence as context, not truth or authorization. Do not write the reading to Typeform, and stop before any Typeform edit or publication until I separately approve the exact change.",
14
+ successCondition: "The researcher receives at most one optional signal about their own proposition and chooses the next step; no respondent data or SomaCheck result enters Typeform, and no Typeform state changes without separate explicit approval.",
15
+ },
16
+ {
17
+ id: "dovetail-research-reflection-v1",
18
+ title: "Dovetail + SomaCheck Research Reflection",
19
+ guidePath: "recipes/dovetail-research-reflection.md",
20
+ purpose: "Privately reflect on the researcher's own Dovetail planning document or evidence interpretation.",
21
+ phoneHolder: "The phone holder is the researcher reflecting on their own work, never a participant or employee.",
22
+ prerequisites: [
23
+ "SomaCheck is ready on the researcher's iPhone and the vibecheck MCP is linked to that researcher's agent client.",
24
+ "The researcher has separately connected and authorized Dovetail MCP for the intended workspace; this command does not connect or authorize it.",
25
+ "The researcher can name one researcher-authored, participant-free Dovetail brief, guide, plan, or de-identified synthesis.",
26
+ ],
27
+ starterPrompt: "Review only my researcher-authored Dovetail doc [DOC TITLE OR ID]. Do not open raw interviews, transcripts, recordings, highlights, contacts, identifiers, participant responses, or any broader workspace content. Separate what the document says from what you infer, then help me privately reflect on my own interpretation or next step with one optional SomaCheck vibecheck. If you propose a vibecheck, show me the exact first-person proposition and wait for my acceptance. Treat Aligned or Unaligned plus confidence as context, not truth or authorization. Keep the reading out of Dovetail, and do not create a comment or make another Dovetail change unless I separately approve its exact signal-free content.",
28
+ successCondition: "The researcher receives at most one optional signal about their own proposition and chooses the next step; no participant material or SomaCheck result enters the cross-MCP flow, and Dovetail changes require separate explicit approval.",
29
+ },
30
+ {
31
+ id: "prolific-research-reflection-v1",
32
+ title: "Prolific + SomaCheck Research Reflection",
33
+ guidePath: "recipes/prolific-research-reflection.md",
34
+ purpose: "Privately reflect on the researcher's own Prolific study or recruitment plan before formal review.",
35
+ phoneHolder: "The phone holder is the researcher reflecting on their own work, never a participant or employee.",
36
+ prerequisites: [
37
+ "SomaCheck is ready on the researcher's iPhone and the vibecheck MCP is linked to that researcher's agent client.",
38
+ "The researcher has separately installed, reviewed, pinned, and authorized the official Prolific MCP server; this command does not install, connect, or authorize it.",
39
+ "The researcher can name one researcher-owned workspace, project, or draft study without retrieving submissions, messages, participant identifiers, responses, or demographic exports.",
40
+ ],
41
+ starterPrompt: "Review my researcher-owned Prolific [WORKSPACE, PROJECT, OR DRAFT STUDY NAME OR ID] using only minimum study-planning information. Do not retrieve participant-level data or use a SomaCheck result for recruitment, filtering, eligibility, payment, quality, authenticity, or performance. Separate the study facts from your inferences, then help me privately reflect on my own plan or readiness with one optional SomaCheck vibecheck. If you propose a vibecheck, show me the exact first-person proposition and wait for my acceptance. Treat Aligned or Unaligned plus confidence as context, not truth or authorization. Do not create filters or studies, publish, spend money, or write the reading to Prolific.",
42
+ successCondition: "The researcher receives at most one optional signal about their own proposition and chooses the next step; no participant data or SomaCheck result enters Prolific, and the recipe creates no filter, study, publication, or spend.",
43
+ },
44
+ {
45
+ id: "maze-research-reflection-v1",
46
+ title: "Maze + SomaCheck Research Reflection",
47
+ guidePath: "recipes/maze-research-reflection.md",
48
+ purpose: "Privately reflect on the researcher's interpretation of one Maze study using metadata or one aggregate summary, with no Maze mutation.",
49
+ phoneHolder: "The phone holder is the researcher reflecting on their own interpretation plan, never a participant, respondent, tester, or employee.",
50
+ prerequisites: [
51
+ "SomaCheck is ready on the researcher's iPhone and the vibecheck MCP is linked to that researcher's agent client.",
52
+ "The product or integration owner has provisioned an organization-authorized Maze connection or partner sandbox at https://connect.maze.co/mcp; running this recipe does not ask the researcher to sign up, upgrade, administer access, or populate Maze data.",
53
+ "The provisioned workspace supplies one named existing study with safe metadata or an aggregate summary; if it does not, the Maze half remains untested and the researcher still receives a copy-ready plan.",
54
+ ],
55
+ starterPrompt: "Use Maze and SomaCheck to help me reflect on my own interpretation plan for one named Maze study. I am the researcher and I am holding the phone. The study is [STUDY NAME OR ID PROVIDED BY THE WORKSPACE OWNER]. Use only the preconfigured official hosted read-only Maze MCP at https://connect.maze.co/mcp. Read only that study's safe metadata or one aggregate summary. Do not retrieve transcripts, recordings, heatmaps, click maps, mission answers, participant identifiers, contact fields, demographic exports, free text, or row-level data. Label what Maze literally reports separately from your interpretation, then offer two defensible framings. Offer one short first-person proposition about my own interpretation plan and wait for my acceptance before any proactive SomaCheck ask. Treat Aligned or Unaligned plus confidence as context, not truth, diagnosis, authorization, or evidence about a participant. Ask what I choose in words. Keep the proposition, reading, confidence, and confirmation out of Maze. Do not create, edit, publish, delete, archive, export, share, tag, comment, or otherwise write to Maze. If the preconfigured connection or a safe runtime read is absent, label the Maze half untested, return a copy-ready interpretation plan, and tell the integration owner what capability is missing. Do not ask me to sign up, upgrade, administer access, or populate Maze data.",
56
+ successCondition: "The researcher receives at most one optional signal about their own proposition and states a typed choice after a real metadata or aggregate read from a preconfigured workspace; no participant data or SomaCheck output enters Maze, and the Maze workspace remains unchanged.",
57
+ },
58
+ {
59
+ id: "chattermill-research-reflection-v1",
60
+ title: "Chattermill + SomaCheck Research Reflection",
61
+ guidePath: "recipes/chattermill-research-reflection.md",
62
+ purpose: "Privately reflect on aggregate Chattermill metrics or generated highlights while excluding individual feedback and every write path.",
63
+ phoneHolder: "The phone holder is the researcher reflecting on their own interpretation, never a customer, respondent, participant, or employee.",
64
+ prerequisites: [
65
+ "SomaCheck is ready on the researcher's iPhone and the vibecheck MCP is linked to that researcher's agent client.",
66
+ "The product or integration owner has provisioned an organization-authorized Chattermill connection or partner sandbox at https://app.chattermill.com/mcp with mcp:read; running this recipe does not ask the researcher to sign up, purchase access, administer access, or populate Chattermill data.",
67
+ "The provisioned workspace supplies one named existing project for which get_metrics or generate_highlights is sufficient; get_feedback, search_observations, identifier-bearing attributes, and free text remain prohibited.",
68
+ ],
69
+ starterPrompt: "Use Chattermill and SomaCheck to help me reflect on my own interpretation of one named project's customer-feedback insights. I am the researcher and I am holding the phone. The project is [PROJECT NAME OR ID PROVIDED BY THE WORKSPACE OWNER]. Use only the preconfigured official hosted read-only Chattermill MCP at https://app.chattermill.com/mcp. Read only aggregate metrics with get_metrics or generated highlights with generate_highlights, plus the minimum discovery metadata needed to build that query. Never call get_feedback or search_observations, enumerate identifier-bearing attributes, or surface individual feedback, source quotes, respondent identifiers, contact fields, or free text. Label what the aggregate output literally reports separately from your interpretation, then offer two defensible interpretations. Offer one short first-person proposition about my own interpretation or next step and wait for my acceptance before any proactive SomaCheck ask. Treat Aligned or Unaligned plus confidence as context, not truth, diagnosis, authorization, or evidence about an individual customer. Ask what I choose in words. Keep the proposition, reading, confidence, and confirmation out of Chattermill. Do not create, edit, publish, delete, share, export, tag, comment, or otherwise write to Chattermill. If the preconfigured connection, mcp:read, or a safe aggregate runtime tool is absent, label the Chattermill half untested, return a copy-ready interpretation, and tell the integration owner what capability is missing. Do not ask me to sign up, purchase access, administer access, or populate Chattermill data.",
70
+ successCondition: "The researcher receives at most one optional signal about their own proposition and states a typed choice after a real aggregate or generated-summary read from a preconfigured workspace; no individual feedback or SomaCheck output enters the cross-MCP flow, and Chattermill remains unchanged.",
71
+ },
72
+ {
73
+ id: "user-interviews-research-reflection-v1",
74
+ title: "User Interviews + SomaCheck Research Reflection",
75
+ guidePath: "recipes/user-interviews-research-reflection.md",
76
+ purpose: "Privately reflect on the researcher's own User Interviews study framing and produce a copy-ready test-project plan without calling a platform mutation.",
77
+ phoneHolder: "The phone holder is the researcher reflecting on their own study direction, never a respondent, participant, candidate, or employee.",
78
+ prerequisites: [
79
+ "SomaCheck is ready on the researcher's iPhone and the vibecheck MCP is linked to that researcher's agent client.",
80
+ "The planning workflow requires no User Interviews account or connector because it calls no platform data or action tool.",
81
+ "For partner integration validation only, the product or integration owner may supply a preconfigured partner sandbox whose runtime capability list can be inspected without invoking a project, recruitment, participant-data, contact, publication, or spend tool; the researcher is never asked to request access or populate platform data.",
82
+ ],
83
+ starterPrompt: "Use User Interviews and SomaCheck to help me prepare one clearly named, participant-free test-project plan without changing User Interviews. I am the researcher and I am holding the phone. The study idea is [STUDY IDEA]. Work only from what I provide here. If a product or integration owner has supplied a preconfigured official User Interviews partner sandbox, you may inspect its capability names and schemas, but do not call any platform data or action tool. The planning workflow does not require me to sign up, request access, purchase a plan, or populate User Interviews data. Do not retrieve candidates, participants, screeners, responses, profiles, identifiers, messages, recordings, transcripts, session data, or other workspace data. Propose two neutral study framings and show the exact signal-free project copy. Then offer one short first-person proposition about my own preferred direction and wait for my acceptance before any proactive SomaCheck ask. Treat Aligned or Unaligned plus confidence as context, not truth, diagnosis, authorization, or evidence about another person. Ask what I choose in words. Keep the proposition, reading, confidence, and confirmation out of User Interviews. Return a copy-ready project payload only. Do not create, edit, recruit, invite, message, schedule, screen, launch, publish, attach incentives, spend, read participant data, or call a broader or undocumented tool.",
84
+ successCondition: "The researcher receives at most one optional signal about their own proposition and states a typed choice; the agent returns a copy-ready signal-free test-project plan, invokes no User Interviews data or action tool, and performs no recruitment, participant read, contact, mutation, launch, publication, incentive, payment, or spend.",
85
+ },
86
+ {
87
+ id: "sprig-research-reflection-v1",
88
+ title: "Sprig + SomaCheck Research Reflection",
89
+ guidePath: "recipes/sprig-research-reflection.md",
90
+ purpose: "Privately reflect on an owner-supplied, participant-free Sprig test artifact and return a copy-ready revision without changing Sprig.",
91
+ phoneHolder: "The phone holder is the researcher reflecting on their own study direction, never a respondent or employee.",
92
+ prerequisites: [
93
+ "SomaCheck is ready on the researcher's iPhone and the vibecheck MCP is linked to that researcher's agent client.",
94
+ "The product or integration owner has provisioned an organization-authorized Sprig connection or partner sandbox; this workflow does not ask the researcher to sign up, upgrade, purchase access, administer access, or populate Sprig data.",
95
+ "The owner supplies one clearly named, participant-free test artifact and a least-privilege runtime tool that can read only the artifact metadata and question text needed for the review.",
96
+ ],
97
+ starterPrompt: "Use Sprig and SomaCheck to help me privately reflect on one participant-free test artifact supplied by the product or integration owner. I am the researcher and I am holding the phone. The artifact is [TEST ARTIFACT NAME OR ID PROVIDED BY THE OWNER]. Use only the preconfigured Sprig partner sandbox or organization-authorized connection. Read only the minimum artifact metadata and question text needed for this review. Do not retrieve responses, themes, source quotes, respondent fields, identifiers, contact data, or PII. Offer two defensible wording directions, then offer one short first-person proposition about my own direction and wait for my acceptance before any proactive SomaCheck ask. After the check, use only the proposition, Aligned or Unaligned, and confidence together with our conversation context; exercise your judgment about the next conversational move and do not apply a fixed script to either result. I decide what the signal means and what happens next. Keep the proposition, reading, confidence, and confirmation out of Sprig. Return a copy-ready, signal-free revision only. Do not create, edit, publish, delete, share, archive, export, duplicate, target, distribute, launch, contact anyone, or otherwise write to Sprig. If the preconfigured connection or safe read tool is absent, label the Sprig half untested, work only from a participant-free brief I provide, and tell the integration owner what capability is missing. Do not ask me to sign up, upgrade, purchase access, administer access, or populate Sprig data.",
98
+ successCondition: "The researcher receives at most one optional signal about their own proposition, the agent retains judgment about the next conversational move, and the researcher receives a copy-ready signal-free revision after a narrow read of an owner-supplied participant-free test artifact; no research data or SomaCheck output crosses systems, and Sprig remains unchanged.",
99
+ },
100
+ {
101
+ id: "great-question-research-reflection-v1",
102
+ title: "Great Question + SomaCheck Research Reflection",
103
+ guidePath: "recipes/great-question-research-reflection.md",
104
+ purpose: "Privately reflect on a survey framing using one owner-provisioned, participant-free Great Question test artifact without changing Great Question.",
105
+ phoneHolder: "The phone holder is the researcher reflecting on their own study framing, never a candidate, participant, or employee.",
106
+ prerequisites: [
107
+ "SomaCheck is ready on the researcher's iPhone and the vibecheck MCP is linked to that researcher's agent client.",
108
+ "The product or integration owner has provisioned an organization-authorized Great Question connection or partner sandbox at https://greatquestion.co/api/mcp/v1 with PII hiding enabled; running this recipe does not ask the researcher to sign up, upgrade, purchase, administer access, or populate Great Question data.",
109
+ "The owner supplies one participant-free test study, and the client restricts the platform half to get_survey_study for that exact artifact; no discovery or mutation tool is approved.",
110
+ ],
111
+ starterPrompt: "Use Great Question and SomaCheck to help me reflect on a five-question test survey plan without changing Great Question. I am the researcher and I am holding the phone. The product or integration owner has provisioned a partner sandbox and named this participant-free test study: [TEST STUDY NAME OR ID]. Read only that study's safe title, purpose, and question structure with get_survey_study. Do not search or list the workspace, and do not read candidates, participants, screeners, responses, sessions, transcripts, recordings, highlights, insights, reels, or other workspace data. Propose two neutral study framings and show their exact questions. Then offer one short first-person proposition about my own preferred framing and wait for my acceptance before any proactive SomaCheck ask. Give the agent the Aligned or Unaligned result plus confidence as context and let it use that context with its judgment. Ask what I choose in words. Keep the proposition, reading, confidence, and my confirmation out of Great Question. Return a signal-free, copy-ready research plan only. Do not create, update, delete, recruit, invite, message, schedule, incentivize, launch, publish, or otherwise write to Great Question. Do not ask me to sign up, upgrade, purchase, administer access, or populate Great Question data.",
112
+ successCondition: "The researcher receives at most one optional signal, states a choice, and receives a copy-ready five-question plan; only get_survey_study reads the exact owner-supplied test artifact, Great Question remains unchanged, and no recruitment or participant data is accessed.",
113
+ },
114
+ {
115
+ id: "questionpro-research-reflection-v1",
116
+ title: "QuestionPro + SomaCheck Research Reflection",
117
+ guidePath: "recipes/questionpro-research-reflection.md",
118
+ purpose: "Privately compare questionnaire wording and return a copy-ready, non-distributed survey plan for a partner-owned evaluation.",
119
+ phoneHolder: "The phone holder is the researcher reflecting on their own questionnaire wording, never a respondent, audience member, or employee.",
120
+ prerequisites: [
121
+ "SomaCheck is ready on the researcher's iPhone and the vibecheck MCP is linked to that researcher's agent client.",
122
+ "A QuestionPro or integration owner supplies a partner-approved sandbox or schema fixture; the researcher is not asked to sign up, upgrade, or populate vendor data.",
123
+ "Any live discovery is limited to schemas at https://mcp.questionpro.com/surveybuilder, never a root or all-tools endpoint.",
124
+ ],
125
+ starterPrompt: "Use QuestionPro and SomaCheck to help me prepare a four-question, participant-free test-survey plan without changing QuestionPro. I am the researcher and I am holding the phone. The goal is [GOAL]. Work only from what I provide. If a product or integration owner supplied a preconfigured official QuestionPro partner sandbox, inspect only focused Survey Builder capability names and schemas; call no platform data or action tool. Do not ask me to sign up, upgrade, purchase access, administer access, or populate QuestionPro. Show two questionnaire framings, then offer one short first-person proposition about my own direction and wait for my acceptance before a proactive SomaCheck ask. Give Aligned or Unaligned plus confidence to the agent as context and let it use its judgment. Ask what I choose and return the exact signal-free survey definition. Do not create, edit, send, distribute, publish, recruit, launch, access responses, spend, or write the reading to QuestionPro.",
126
+ successCondition: "The researcher receives at most one optional signal, states a choice, and receives a copy-ready four-question plan; no QuestionPro data or action tool runs, and partner interoperability remains untested without a partner-owned sandbox.",
127
+ },
128
+ {
129
+ id: "spotify-listening-reflection-v1",
130
+ title: "Spotify Studio/Kit + SomaCheck Listening Reflection",
131
+ guidePath: "recipes/spotify-listening-reflection.md",
132
+ purpose: "Use one consented signal while choosing a next song or personal audio direction, while the listener keeps final control of every Spotify action.",
133
+ phoneHolder: "The phone holder is the listener reflecting on their own listening choice; the signal never assesses an artist, creator, or another listener.",
134
+ prerequisites: [
135
+ "SomaCheck is ready on the listener's iPhone and the vibecheck MCP is linked to the companion agent.",
136
+ "For the Studio pilot, the listener has access to Studio by Spotify Labs; direct third-party MCP attachment is not publicly documented, so the current Kit handoff is manual.",
137
+ "For automation outside Studio, use the separately reviewed local Spotify companion with an allowlisted Premium account and active device, or Spotify's official Save to Spotify skill for personal talk audio.",
138
+ ],
139
+ starterPrompt: "Kit, suggest one specific track based on my Spotify taste and what is playing, but do not change Spotify. I will ask my SomaCheck-capable companion to send exactly: ‘I want to hear this song next.’ Treat any result I choose to relay only as context—not emotion, cause, instruction, or authorization—and do not remember it as a stable preference. Then ask me whether to play the exact track and wait for my yes. If Kit cannot call SomaCheck directly, label this a manual bridge; do not claim a native MCP integration.",
140
+ successCondition: "The listener receives one signal about their own proposition, decides whether to share it, and separately confirms the exact Spotify action; both Aligned and Unaligned stop at the same human choice, with no automatic playback or emotional inference.",
141
+ },
142
+ ];
143
+ export function getRecipeExperience(id) {
144
+ return RECIPE_CATALOG.find((recipe) => recipe.id === id) ?? null;
145
+ }
146
+ export function formatRecipeList() {
147
+ const entries = RECIPE_CATALOG.map((recipe) => [
148
+ ` ${recipe.id}`,
149
+ ` ${recipe.title}`,
150
+ ` ${recipe.purpose}`,
151
+ ].join("\n"));
152
+ return [
153
+ "SomaCheck customer-testable recipes",
154
+ "",
155
+ ...entries,
156
+ "",
157
+ "These commands print prompt guides. They do not install, connect, or authorize a partner MCP server, call either MCP, or change partner-platform state.",
158
+ "In every recipe, the phone holder is the person reflecting on their own choice.",
159
+ "",
160
+ "Print one recipe:",
161
+ " npx -y @somacheck/vibecheck recipe <id>",
162
+ ].join("\n");
163
+ }
164
+ export function formatRecipeDetail(recipe) {
165
+ return [
166
+ recipe.title,
167
+ `Recipe ID: ${recipe.id}`,
168
+ `Packaged guide: ${recipe.guidePath}`,
169
+ "",
170
+ "Purpose:",
171
+ recipe.purpose,
172
+ "",
173
+ "Phone holder:",
174
+ recipe.phoneHolder,
175
+ "",
176
+ "Prerequisites:",
177
+ ...recipe.prerequisites.map((prerequisite, index) => `${index + 1}. ${prerequisite}`),
178
+ "",
179
+ "Starter prompt (copy exactly, replacing the bracketed scope):",
180
+ recipe.starterPrompt,
181
+ "",
182
+ "Success condition:",
183
+ recipe.successCondition,
184
+ "",
185
+ "This CLI command only prints this guide. It does not install, connect, or authorize a partner MCP server, call either MCP, or change partner-platform state.",
186
+ ].join("\n");
187
+ }
188
+ export function formatRecipePrompt(recipe) {
189
+ return recipe.starterPrompt;
190
+ }
191
+ //# sourceMappingURL=recipes.js.map
package/dist/server.js CHANGED
@@ -2,7 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { z } from "zod";
3
3
  import { StatementPendingError, } from "./vibecheck.js";
4
4
  import { SomaCheckCompatibilityError, SomaCheckHttpError, SomaCheckLiveAskClientError, SomaCheckLiveAskConflictError, SomaCheckLiveAskPendingError, } from "./api.js";
5
- import { PACKAGE_NAME, PACKAGE_SPEC, PACKAGE_VERSION } from "./constants.js";
5
+ import { PACKAGE_NAME, PACKAGE_SPEC, PACKAGE_VERSION, SOMACHECK_SETUP_URL } from "./constants.js";
6
6
  // A human gesture (unlock phone, read the statement, make the wrist gesture)
7
7
  // reliably takes longer than the old 10s budget, so an inline wait almost
8
8
  // never resolves before the tool returns pending. The MCP SDK's documented
@@ -71,8 +71,8 @@ const resultSchema = {
71
71
  request_id: z.string(),
72
72
  status: z.enum(["queued", "pending", "answered", "expired", "cancelled"]),
73
73
  verdict: z.enum(["aligned", "unaligned"]).nullable(),
74
- confidence: z.number().nullable(),
75
- latency_s: z.number().nullable(),
74
+ confidence: z.number().min(0).max(1).nullable(),
75
+ latency_s: z.number().min(0).nullable(),
76
76
  user_feedback: z.enum(["agreed", "disagreed", "not_provided"]).nullable().optional(),
77
77
  };
78
78
  const contextItemSchema = z.object({
@@ -82,7 +82,7 @@ const contextItemSchema = z.object({
82
82
  confidence: z.number().min(0).max(1),
83
83
  answered_at: z.string().datetime({ offset: true }),
84
84
  });
85
- const contextSchema = { checkins: z.array(contextItemSchema) };
85
+ const contextSchema = { checkins: z.array(contextItemSchema).max(20) };
86
86
  const liveAskErrorCodeSchema = z.enum([
87
87
  "link_revoked",
88
88
  "upgrade_required",
@@ -119,9 +119,8 @@ const agentContextObservationSchema = z.object({
119
119
  evidence_count: z.number().int().min(1).max(100).default(1),
120
120
  });
121
121
  const contextShareSchema = {
122
- source_id: z.string(), entry_id: z.string(), accepted: z.boolean(),
122
+ accepted: z.boolean(),
123
123
  state: z.literal("ready"), observation_count: z.number().int().min(1).max(20),
124
- captured_at: z.string().datetime({ offset: true }),
125
124
  };
126
125
  const SERVER_INSTRUCTIONS = [
127
126
  "SomaCheck lets you ask your person for a vibecheck.",
@@ -227,17 +226,22 @@ export function createVibecheckServer(dependencies) {
227
226
  outputSchema: contextShareSchema,
228
227
  annotations: {
229
228
  readOnlyHint: false,
230
- destructiveHint: false,
231
- idempotentHint: true,
229
+ destructiveHint: true,
230
+ idempotentHint: false,
232
231
  openWorldHint: false,
233
232
  },
234
233
  }, async ({ observations }) => {
235
234
  try {
236
235
  const token = await dependencies.loadToken();
237
236
  const result = await dependencies.api.shareContext(token, observations);
237
+ const structuredContent = {
238
+ accepted: result.accepted,
239
+ state: result.state,
240
+ observation_count: result.observation_count,
241
+ };
238
242
  return {
239
243
  content: [{ type: "text", text: `Shared ${result.observation_count} bounded context observation${result.observation_count === 1 ? "" : "s"}. SomaCheck can now prepare contextual propositions.` }],
240
- structuredContent: { ...result },
244
+ structuredContent,
241
245
  };
242
246
  }
243
247
  catch (error) {
@@ -310,7 +314,9 @@ export function createVibecheckServer(dependencies) {
310
314
  : result.status === "queued"
311
315
  ? "Queued. The proposition is cached until the person advances their feed."
312
316
  : result.status === "pending"
313
- ? "Pending. The person has not completed this check-in yet."
317
+ ? liveRequestId === null
318
+ ? "Pending. The person has not completed this check-in yet."
319
+ : pendingWaitText(request_id, null, false)
314
320
  : `The request is ${result.status}.`;
315
321
  return { content: [{ type: "text", text }], structuredContent };
316
322
  }
@@ -320,7 +326,8 @@ export function createVibecheckServer(dependencies) {
320
326
  });
321
327
  server.registerTool("request_vibecheck", {
322
328
  title: "Request a Vibecheck",
323
- description: "Send one statement to the person's phone for a SomaCheck vibecheck. If the person asks for a vibecheck, choose a useful first-person statement from your available context and send it. For proactive offers, call only after the person accepts. The result is context, not authorization.",
329
+ description: "Send one statement to the person's phone for a SomaCheck vibecheck. If the person asks for a vibecheck, choose a useful first-person statement from your available context and send it. For proactive offers, call only after the person accepts. The result is context, not authorization. "
330
+ + "This call waits up to 45 seconds. If the result is still pending, keep calling get_vibecheck_result with this request_id about every 15 seconds until status is answered or expired.",
324
331
  inputSchema: {
325
332
  statement: z.string().trim().min(1).max(1000)
326
333
  .describe("One plain-language first-person statement for the person to test. Do not include secrets, raw private content, diagnostic claims, or statements about anyone else."),
@@ -332,7 +339,7 @@ export function createVibecheckServer(dependencies) {
332
339
  outputSchema: liveAskSchema,
333
340
  annotations: {
334
341
  readOnlyHint: false,
335
- destructiveHint: false,
342
+ destructiveHint: true,
336
343
  idempotentHint: true,
337
344
  openWorldHint: false,
338
345
  },
@@ -379,7 +386,10 @@ export function createVibecheckServer(dependencies) {
379
386
  ? `Vibecheck completed: ${result.verdict}, confidence ${formatChannelConfidence(result.confidence)}${formatUserFeedback(result.user_feedback)}.`
380
387
  : result.state === "pending"
381
388
  ? result.delivery_state === "failed" || result.delivery_state === "skipped"
382
- ? `Vibecheck ${result.request_id} was stored, but phone delivery is ${result.delivery_state}. It expires at ${result.expires_at}.`
389
+ ? `Vibecheck ${result.request_id} was stored, but phone delivery is ${result.delivery_state}. `
390
+ + `Open SomaCheck on your phone, signed into the same account, and check Home. `
391
+ + `If you do not have the app, install it at ${SOMACHECK_SETUP_URL} and finish phone setup. `
392
+ + `Keep this request ID; do not create a duplicate or claim the phone received it. It expires at ${result.expires_at}.`
383
393
  : pendingWaitText(result.request_id, result.expires_at, supportsClaudeChannel)
384
394
  : `This vibecheck is ${result.state}.`;
385
395
  return { content: [{ type: "text", text }], structuredContent };
@@ -467,10 +477,12 @@ function formatChannelConfidence(confidence) {
467
477
  function formatUserFeedback(feedback) {
468
478
  return feedback === undefined || feedback === null ? "" : `, user feedback: ${feedback}`;
469
479
  }
470
- /** Text for a freshly created, still-pending Live Ask, once delivery is not
471
- * failed/skipped. `hasLocalWatch` is true only for the local stdio server
472
- * when the Claude PostToolUse hook's background watch is armed for this
473
- * request (see startClaudeChannelWatch) that watch notifies the same
480
+ /** Text for a still-pending Live Ask, whether just created (delivery not
481
+ * failed/skipped) or re-read via get_vibecheck_result. `expiresAt` is null
482
+ * when the caller has no expiry to report (get_vibecheck_result's live read
483
+ * does not receive one). `hasLocalWatch` is true only for the local stdio
484
+ * server when the Claude PostToolUse hook's background watch is armed for
485
+ * this request (see startClaudeChannelWatch) — that watch notifies the same
474
486
  * session automatically, so the agent should not be told to poll. The hosted
475
487
  * edge function is stateless per request and never has a watch, so it must
476
488
  * tell the agent exactly what to do next instead of inviting it to ask the
@@ -480,9 +492,11 @@ function pendingWaitText(requestId, expiresAt, hasLocalWatch) {
480
492
  return `Vibecheck ${requestId} is pending. It expires at ${expiresAt}. This session will be notified automatically when the person responds.`;
481
493
  }
482
494
  return `Vibecheck ${requestId} is pending. SomaCheck has not received a response yet. `
483
- + `Call get_vibecheck_result with request_id "${requestId}" again in about 15 seconds, `
495
+ + `If no phone prompt appears, open SomaCheck on the same account; phone setup is at ${SOMACHECK_SETUP_URL}. `
496
+ + `Call get_vibecheck_result with request_id ${requestId} again in about 15 seconds, `
484
497
  + `and keep polling at that cadence until the state is answered or expired. `
485
- + `It expires at ${expiresAt}.`;
498
+ + `Do not end your turn or ask the person whether to check; they are answering on their phone.`
499
+ + (expiresAt === null ? "" : ` It expires at ${expiresAt}.`);
486
500
  }
487
501
  async function waitForLiveVibecheck(created, token, identity, dependencies, signal, deadline, progress) {
488
502
  if (created.state !== "pending"