@somacheck/vibecheck 0.6.8 → 0.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -9
- package/claude-marketplace/.claude-plugin/marketplace.json +1 -1
- package/claude-marketplace/plugins/vibecheck/.claude-plugin/plugin.json +1 -1
- package/claude-marketplace/plugins/vibecheck/hooks/hooks.json +1 -1
- package/dist/client-setup.js +4 -1
- package/dist/constants.js +1 -1
- package/dist/readiness.js +27 -0
- package/dist/server.js +46 -11
- package/package.json +1 -1
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.
|
|
12
|
+
**What the `link` command does.** `npx -y @somacheck/vibecheck@0.6.10 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.
|
|
101
|
-
npx -y @somacheck/vibecheck@0.6.
|
|
102
|
-
npx -y @somacheck/vibecheck@0.6.
|
|
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
|
|
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.
|
|
112
|
+
"args": ["-y", "@somacheck/vibecheck@0.6.10", "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.
|
|
118
|
+
For Claude Code: `claude mcp add --scope user vibecheck -- npx -y @somacheck/vibecheck@0.6.10 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
|
|
@@ -218,9 +218,14 @@ once after setup so it loads the plugin.
|
|
|
218
218
|
|
|
219
219
|
Other standard MCP hosts retain the bounded tool response and stable-handle
|
|
220
220
|
fallback until they provide an equivalent supported wake mechanism. This
|
|
221
|
-
Claude-specific continuation must never be presented as provider-neutral.
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
Claude-specific continuation must never be presented as provider-neutral. A
|
|
222
|
+
pending response without that hook (including the hosted connector, which is
|
|
223
|
+
stateless per request and never arms the watch) tells the agent the exact
|
|
224
|
+
`request_id` and the person's expiry, and to call `get_vibecheck_result` again
|
|
225
|
+
in about 15 seconds, repeating at that cadence until the state is answered or
|
|
226
|
+
expired.
|
|
227
|
+
|
|
228
|
+
The 45-second whole-call deadline also covers the one create request. If that
|
|
224
229
|
network request is aborted after an ambiguous commit, retry the exact same
|
|
225
230
|
statement with the same `idempotency_key`.
|
|
226
231
|
|
|
@@ -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.
|
|
13
|
+
"version": "0.6.10",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Sensie",
|
|
16
16
|
"email": "agents@joinsensie.com"
|
package/dist/client-setup.js
CHANGED
|
@@ -146,7 +146,10 @@ function isManagedSomaCheckRegistration(client, stdout) {
|
|
|
146
146
|
|| packageArg === "@somacheck/vibecheck@0.6.3"
|
|
147
147
|
|| packageArg === "@somacheck/vibecheck@0.6.4"
|
|
148
148
|
|| packageArg === "@somacheck/vibecheck@0.6.5"
|
|
149
|
-
|| packageArg === "@somacheck/vibecheck@0.6.6"
|
|
149
|
+
|| packageArg === "@somacheck/vibecheck@0.6.6"
|
|
150
|
+
|| packageArg === "@somacheck/vibecheck@0.6.7"
|
|
151
|
+
|| packageArg === "@somacheck/vibecheck@0.6.8"
|
|
152
|
+
|| packageArg === "@somacheck/vibecheck@0.6.9")
|
|
150
153
|
&& args.length === 5
|
|
151
154
|
&& args[0] === "-y"
|
|
152
155
|
&& args[1] === packageArg
|
package/dist/constants.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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.
|
|
5
|
+
export const PACKAGE_VERSION = "0.6.10";
|
|
6
6
|
export const PACKAGE_SPEC = `${PACKAGE_NAME}@${PACKAGE_VERSION}`;
|
|
7
7
|
export const MCP_SERVER_NAME = "vibecheck";
|
|
8
8
|
export const LEGACY_HOSTED_MCP_SERVER_NAME = "somacheck";
|
package/dist/readiness.js
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
1
3
|
import { readConfig } from "./config.js";
|
|
2
4
|
import { SomaCheckCompatibilityError, SomaCheckHttpError } from "./api.js";
|
|
3
5
|
import { clientDisplayName, clientRegistrationState, detectLegacyHostedRegistration, isClientInstalled, manualLegacyHostedRemoveCommand, manualRemoveCommand, manualSetupCommand, } from "./client-setup.js";
|
|
4
6
|
import { BACKEND_PROTOCOL_VERSION, PACKAGE_VERSION } from "./constants.js";
|
|
7
|
+
function vibecheckRegistrationCount(value) {
|
|
8
|
+
if (!value || typeof value !== "object")
|
|
9
|
+
return 0;
|
|
10
|
+
let count = 0;
|
|
11
|
+
for (const [key, child] of Object.entries(value)) {
|
|
12
|
+
if (key === "mcpServers" && child && typeof child === "object" && !Array.isArray(child)) {
|
|
13
|
+
count += Object.keys(child).filter((name) => name.startsWith("vibecheck")).length;
|
|
14
|
+
}
|
|
15
|
+
count += vibecheckRegistrationCount(child);
|
|
16
|
+
}
|
|
17
|
+
return count;
|
|
18
|
+
}
|
|
19
|
+
async function claudeVibecheckRegistrationCount(home) {
|
|
20
|
+
try {
|
|
21
|
+
return vibecheckRegistrationCount(JSON.parse(await readFile(join(home, ".claude.json"), "utf8")));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
5
27
|
export async function checkReadiness(dependencies) {
|
|
6
28
|
let token;
|
|
7
29
|
try {
|
|
@@ -49,6 +71,11 @@ export async function checkReadiness(dependencies) {
|
|
|
49
71
|
}
|
|
50
72
|
}
|
|
51
73
|
const backendReachable = statusProbeOk && contextProbeOk;
|
|
74
|
+
const claudeRegistrationCount = await claudeVibecheckRegistrationCount(dependencies.home);
|
|
75
|
+
if (claudeRegistrationCount > 1) {
|
|
76
|
+
dependencies.output(`○ Multiple Claude Code registrations whose names start with "vibecheck" were found (${claudeRegistrationCount}).`);
|
|
77
|
+
dependencies.output(" Remove stale entries from ~/.claude.json so only the current user-scope vibecheck registration remains.");
|
|
78
|
+
}
|
|
52
79
|
const clients = ["codex", "claude"];
|
|
53
80
|
let installedClientCount = 0;
|
|
54
81
|
let hasLegacyHostedRegistration = false;
|
package/dist/server.js
CHANGED
|
@@ -3,9 +3,21 @@ import { z } from "zod";
|
|
|
3
3
|
import { StatementPendingError, } from "./vibecheck.js";
|
|
4
4
|
import { SomaCheckCompatibilityError, SomaCheckHttpError, SomaCheckLiveAskClientError, SomaCheckLiveAskConflictError, SomaCheckLiveAskPendingError, } from "./api.js";
|
|
5
5
|
import { PACKAGE_NAME, PACKAGE_SPEC, PACKAGE_VERSION } from "./constants.js";
|
|
6
|
-
|
|
6
|
+
// A human gesture (unlock phone, read the statement, make the wrist gesture)
|
|
7
|
+
// reliably takes longer than the old 10s budget, so an inline wait almost
|
|
8
|
+
// never resolves before the tool returns pending. The MCP SDK's documented
|
|
9
|
+
// default tool-call timeout is 60s (see LIVE-ASK-CONTRACT.md); 45s leaves a
|
|
10
|
+
// 15s margin for token loading, the create round trip, and result-read
|
|
11
|
+
// overhead on top of the sleeps below, while covering most prompt gestures
|
|
12
|
+
// inline. Overridable via `liveAskWait.budgetMs` so tests stay fast.
|
|
13
|
+
const LIVE_ASK_WAIT_BUDGET_MS = 45_000;
|
|
14
|
+
// Initial backoff, then steady 4s polling. The steady tail's length is sized
|
|
15
|
+
// so the sum matches LIVE_ASK_WAIT_BUDGET_MS above — the wait loop below
|
|
16
|
+
// stops as soon as either the array or the deadline is exhausted, so a
|
|
17
|
+
// smaller injected budgetMs (tests) still ends early on the deadline check.
|
|
7
18
|
const LIVE_ASK_POLL_DELAYS_MS = [
|
|
8
19
|
1_000, 1_500, 2_500, 4_000,
|
|
20
|
+
4_000, 4_000, 4_000, 4_000, 4_000, 4_000, 4_000, 4_000, 4_000,
|
|
9
21
|
];
|
|
10
22
|
const CLAUDE_CHANNEL_POLL_INTERVAL_MS = 2_000;
|
|
11
23
|
const CLAUDE_CHANNEL_MAX_WATCH_MS = 15 * 60 * 1_000;
|
|
@@ -113,12 +125,14 @@ const contextShareSchema = {
|
|
|
113
125
|
};
|
|
114
126
|
const SERVER_INSTRUCTIONS = [
|
|
115
127
|
"SomaCheck lets you ask your person for a vibecheck.",
|
|
116
|
-
"
|
|
117
|
-
"When
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"
|
|
128
|
+
"Offer one when useful or when asked.",
|
|
129
|
+
"When asked, choose and send a useful first-person statement from available context; do not ask for wording unless they want to.",
|
|
130
|
+
"Only request_vibecheck creates an immediate phone ask; post_vibecheck_statement adds optional reflections under Settings → Vibe Checks, never Home.",
|
|
131
|
+
"Help them gain insight from the context you have.",
|
|
132
|
+
"Gesture and optional feedback are context, not authorization.",
|
|
133
|
+
"The SomaCheck account can differ from the host account; never infer its identity from the agent login.",
|
|
134
|
+
"Use your judgment.",
|
|
135
|
+
"Never include secrets, raw private content, diagnostic claims, or assess anyone else.",
|
|
122
136
|
].join(" ");
|
|
123
137
|
export function createVibecheckServer(dependencies) {
|
|
124
138
|
const supportsClaudeChannel = dependencies.identity?.kind === "local"
|
|
@@ -232,7 +246,7 @@ export function createVibecheckServer(dependencies) {
|
|
|
232
246
|
});
|
|
233
247
|
server.registerTool("post_vibecheck_statement", {
|
|
234
248
|
title: "Post Vibecheck Statement",
|
|
235
|
-
description: "Fill
|
|
249
|
+
description: "Fill Settings → Vibe Checks with one to three optional personalized reflections and return immediately. These are quiet feed inventory, never additional Home asks after request_vibecheck. Call get_vibecheck_status first and submit exactly propositions_needed statements.",
|
|
236
250
|
inputSchema: {
|
|
237
251
|
statements: z.array(z.string().min(1)).min(1).max(3)
|
|
238
252
|
.describe("Distinct personalized statements for this person to test, ordered most useful first."),
|
|
@@ -251,7 +265,10 @@ export function createVibecheckServer(dependencies) {
|
|
|
251
265
|
const presented = created.filter((item) => item.presentation_state === "presented").length;
|
|
252
266
|
const queued = created.length - presented;
|
|
253
267
|
return {
|
|
254
|
-
content: [{
|
|
268
|
+
content: [{
|
|
269
|
+
type: "text",
|
|
270
|
+
text: `Added ${created.length} under Settings → Vibe Checks: ${presented} ready, ${queued} cached. No additional Home ask was created.`,
|
|
271
|
+
}],
|
|
255
272
|
structuredContent: { propositions: created },
|
|
256
273
|
};
|
|
257
274
|
}
|
|
@@ -363,7 +380,7 @@ export function createVibecheckServer(dependencies) {
|
|
|
363
380
|
: result.state === "pending"
|
|
364
381
|
? result.delivery_state === "failed" || result.delivery_state === "skipped"
|
|
365
382
|
? `Vibecheck ${result.request_id} was stored, but phone delivery is ${result.delivery_state}. It expires at ${result.expires_at}.`
|
|
366
|
-
:
|
|
383
|
+
: pendingWaitText(result.request_id, result.expires_at, supportsClaudeChannel)
|
|
367
384
|
: `This vibecheck is ${result.state}.`;
|
|
368
385
|
return { content: [{ type: "text", text }], structuredContent };
|
|
369
386
|
}
|
|
@@ -448,7 +465,24 @@ function formatChannelConfidence(confidence) {
|
|
|
448
465
|
return confidence === null ? "unavailable" : confidence.toFixed(2);
|
|
449
466
|
}
|
|
450
467
|
function formatUserFeedback(feedback) {
|
|
451
|
-
return feedback === undefined || feedback === null ? "" : `, user feedback ${feedback}`;
|
|
468
|
+
return feedback === undefined || feedback === null ? "" : `, user feedback: ${feedback}`;
|
|
469
|
+
}
|
|
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
|
|
474
|
+
* session automatically, so the agent should not be told to poll. The hosted
|
|
475
|
+
* edge function is stateless per request and never has a watch, so it must
|
|
476
|
+
* tell the agent exactly what to do next instead of inviting it to ask the
|
|
477
|
+
* human. */
|
|
478
|
+
function pendingWaitText(requestId, expiresAt, hasLocalWatch) {
|
|
479
|
+
if (hasLocalWatch) {
|
|
480
|
+
return `Vibecheck ${requestId} is pending. It expires at ${expiresAt}. This session will be notified automatically when the person responds.`;
|
|
481
|
+
}
|
|
482
|
+
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, `
|
|
484
|
+
+ `and keep polling at that cadence until the state is answered or expired. `
|
|
485
|
+
+ `It expires at ${expiresAt}.`;
|
|
452
486
|
}
|
|
453
487
|
async function waitForLiveVibecheck(created, token, identity, dependencies, signal, deadline, progress) {
|
|
454
488
|
if (created.state !== "pending"
|
|
@@ -497,6 +531,7 @@ async function waitForLiveVibecheck(created, token, identity, dependencies, sign
|
|
|
497
531
|
state: "completed",
|
|
498
532
|
verdict: lifecycle.verdict,
|
|
499
533
|
confidence: lifecycle.confidence,
|
|
534
|
+
user_feedback: lifecycle.user_feedback ?? null,
|
|
500
535
|
};
|
|
501
536
|
}
|
|
502
537
|
if (lifecycle.status === "expired" || lifecycle.status === "cancelled") {
|
package/package.json
CHANGED