@quantiya/codevibe-claude-plugin 1.0.25 → 1.0.26
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/.claude-plugin/plugin.json +1 -1
- package/bin/codevibe-claude +216 -13
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codevibe-claude",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"description": "Sync Claude Code sessions with iOS mobile app via AWS backend. Control Claude Code from your phone with real-time bidirectional synchronization.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "CodeVibe Team"
|
package/bin/codevibe-claude
CHANGED
|
@@ -43,23 +43,118 @@ done
|
|
|
43
43
|
SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
|
|
44
44
|
PLUGIN_DIR="$(dirname "$SCRIPT_DIR")"
|
|
45
45
|
|
|
46
|
+
# ─── Wrapper telemetry (GA4 Measurement Protocol) ─────────────────────
|
|
47
|
+
# Diagnoses agent CLI failures: pre-flight bailouts, fast-die patterns,
|
|
48
|
+
# whether SessionStart hook fired, exit code. Background curl, fail
|
|
49
|
+
# silently, no PII (hashed hostname + per-run random id only). Honors
|
|
50
|
+
# CODEVIBE_TELEMETRY_SOURCE=test for internal testing.
|
|
51
|
+
_CV_MID="G-GS74YEQTB8"
|
|
52
|
+
_CV_SEC="lAfOF6OxRzSQ-NsLBRjhAg"
|
|
53
|
+
_CV_CID="$(echo "$(uname -n)-$(id -u)" | (sha256sum 2>/dev/null || shasum -a 256 2>/dev/null || echo "anonymous-fallback ") | cut -c1-36)"
|
|
54
|
+
_CV_RUN_ID="$(head -c 16 /dev/urandom 2>/dev/null | od -An -tx1 | tr -d ' \n' | cut -c1-32)"
|
|
55
|
+
[ -z "$_CV_RUN_ID" ] && _CV_RUN_ID="fallback-$(date +%s)-$$"
|
|
56
|
+
_CV_AGENT="claude"
|
|
57
|
+
_CV_SOURCE="${CODEVIBE_TELEMETRY_SOURCE:-production}"
|
|
58
|
+
_CV_STARTED_AT="$(date +%s)"
|
|
59
|
+
_CV_EXITED="" # set by terminal events; suppresses trap double-fire
|
|
60
|
+
_CV_PLUGIN_VERSION="$(node -p "require('$PLUGIN_DIR/package.json').version" 2>/dev/null || echo unknown)"
|
|
61
|
+
_CV_MCP_LOG="${CODEVIBE_TMPDIR}/codevibe-claude-mcp.log"
|
|
62
|
+
_CV_MCP_LOG_BASELINE=0
|
|
63
|
+
if [ -f "$_CV_MCP_LOG" ]; then
|
|
64
|
+
_CV_MCP_LOG_BASELINE=$(wc -l < "$_CV_MCP_LOG" 2>/dev/null | tr -d ' ')
|
|
65
|
+
[ -z "$_CV_MCP_LOG_BASELINE" ] && _CV_MCP_LOG_BASELINE=0
|
|
66
|
+
fi
|
|
67
|
+
_CV_TMUX_STARTED="false"
|
|
68
|
+
_CV_AGENT_INVOKED="false"
|
|
69
|
+
_CV_AGENT_STARTED_AT=0
|
|
70
|
+
_CV_CLAUDE_EXIT_FILE="${CODEVIBE_TMPDIR}/codevibe-claude-exit-$$"
|
|
71
|
+
|
|
72
|
+
# Strip an arbitrary string down to a JSON-safe identifier alphabet.
|
|
73
|
+
# Removes anything that could break the hand-built JSON payload below
|
|
74
|
+
# (quotes, backslashes, ANSI escapes, control bytes, tabs, newlines).
|
|
75
|
+
# Truncates to 40 chars to bound the impact of pathological CLI version
|
|
76
|
+
# output. Caller is responsible for emptiness check after sanitize.
|
|
77
|
+
cv_sanitize() {
|
|
78
|
+
printf '%s' "$1" | LC_ALL=C tr -cd 'A-Za-z0-9._\- ' | cut -c1-40
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Sanitize trusted-but-still-string values that go into the payload
|
|
82
|
+
# (plugin version, source label) so future schema additions can't
|
|
83
|
+
# accidentally reintroduce a JSON-injection path.
|
|
84
|
+
_CV_PLUGIN_VERSION="$(cv_sanitize "$_CV_PLUGIN_VERSION")"
|
|
85
|
+
[ -z "$_CV_PLUGIN_VERSION" ] && _CV_PLUGIN_VERSION="unknown"
|
|
86
|
+
_CV_SOURCE="$(cv_sanitize "$_CV_SOURCE")"
|
|
87
|
+
[ -z "$_CV_SOURCE" ] && _CV_SOURCE="production"
|
|
88
|
+
|
|
89
|
+
cv_telem() {
|
|
90
|
+
local event="$1"; shift
|
|
91
|
+
local params="$*"
|
|
92
|
+
curl -s -X POST \
|
|
93
|
+
"https://www.google-analytics.com/mp/collect?measurement_id=${_CV_MID}&api_secret=${_CV_SEC}" \
|
|
94
|
+
-H "Content-Type: application/json" \
|
|
95
|
+
-d "{\"client_id\":\"${_CV_CID}\",\"events\":[{\"name\":\"${event}\",\"params\":{\"agent\":\"${_CV_AGENT}\",\"plugin_version\":\"${_CV_PLUGIN_VERSION}\",\"source\":\"${_CV_SOURCE}\",\"run_id\":\"${_CV_RUN_ID}\"${params:+,$params}}}]}" \
|
|
96
|
+
</dev/null >/dev/null 2>&1 &
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
cv_failed() {
|
|
100
|
+
[ -n "$_CV_EXITED" ] && return 0
|
|
101
|
+
_CV_EXITED="failed"
|
|
102
|
+
cv_telem "wrapper_failed" "\"reason\":\"$1\",\"lifetime_seconds\":$(( $(date +%s) - _CV_STARTED_AT ))"
|
|
103
|
+
}
|
|
104
|
+
|
|
46
105
|
# Handle auth commands (login, logout, status, reset-device)
|
|
47
|
-
# Delegate to codevibe-core CLI
|
|
106
|
+
# Delegate to codevibe-core CLI (shared auth across all plugins).
|
|
107
|
+
# Check both the local node_modules path AND the hoisted location used
|
|
108
|
+
# when the plugin is installed via the @quantiya/codevibe meta-package
|
|
109
|
+
# (parity with the codex/gemini wrappers).
|
|
48
110
|
case "$1" in
|
|
49
111
|
login|logout|status|reset-device)
|
|
50
|
-
# Use codevibe-core CLI
|
|
51
112
|
CORE_CLI="$PLUGIN_DIR/node_modules/@quantiya/codevibe-core/bin/codevibe.js"
|
|
113
|
+
if [ ! -f "$CORE_CLI" ]; then
|
|
114
|
+
CORE_CLI="$PLUGIN_DIR/../codevibe-core/bin/codevibe.js"
|
|
115
|
+
fi
|
|
52
116
|
if [ -f "$CORE_CLI" ]; then
|
|
117
|
+
cv_telem "wrapper_started" "\"invocation\":\"auth_$1\",\"os\":\"$(uname -s | cv_sanitize)\",\"arch\":\"$(uname -m | cv_sanitize)\""
|
|
53
118
|
exec node "$CORE_CLI" "$1"
|
|
54
119
|
else
|
|
55
|
-
echo "Error: codevibe-core not found.
|
|
120
|
+
echo "Error: codevibe-core not found. Try reinstalling: npm install -g @quantiya/codevibe"
|
|
121
|
+
cv_failed "core_not_found"
|
|
122
|
+
sleep 1
|
|
56
123
|
exit 1
|
|
57
124
|
fi
|
|
58
125
|
;;
|
|
59
126
|
esac
|
|
60
127
|
|
|
61
|
-
#
|
|
128
|
+
# Capture environment facts for the session-flow wrapper_started event.
|
|
129
|
+
# Each probe is non-fatal — if a CLI is missing we record "missing" rather
|
|
130
|
+
# than aborting; pre-flight checks below still gate execution. Every
|
|
131
|
+
# string that lands in the JSON payload goes through cv_sanitize so an
|
|
132
|
+
# agent CLI emitting ANSI escapes or quotes in `--version` can't break
|
|
133
|
+
# the hand-built payload.
|
|
134
|
+
_CV_CLAUDE_VER="missing"
|
|
135
|
+
command -v claude >/dev/null 2>&1 && _CV_CLAUDE_VER="$(claude --version 2>/dev/null | cv_sanitize)"
|
|
136
|
+
[ -z "$_CV_CLAUDE_VER" ] && _CV_CLAUDE_VER="unknown"
|
|
137
|
+
_CV_NODE_VER="missing"
|
|
138
|
+
command -v node >/dev/null 2>&1 && _CV_NODE_VER="$(node -v 2>/dev/null | cv_sanitize)"
|
|
139
|
+
[ -z "$_CV_NODE_VER" ] && _CV_NODE_VER="unknown"
|
|
140
|
+
_CV_TMUX_VER="missing"
|
|
141
|
+
command -v tmux >/dev/null 2>&1 && _CV_TMUX_VER="$(tmux -V 2>/dev/null | cv_sanitize)"
|
|
142
|
+
[ -z "$_CV_TMUX_VER" ] && _CV_TMUX_VER="unknown"
|
|
143
|
+
_CV_OS_VER="$(uname -s | cv_sanitize)"
|
|
144
|
+
[ -z "$_CV_OS_VER" ] && _CV_OS_VER="unknown"
|
|
145
|
+
_CV_ARCH_VER="$(uname -m | cv_sanitize)"
|
|
146
|
+
[ -z "$_CV_ARCH_VER" ] && _CV_ARCH_VER="unknown"
|
|
147
|
+
_CV_CLAUDE_CREDS="false"; [ -f "$HOME/.claude/.credentials.json" ] && _CV_CLAUDE_CREDS="true"
|
|
148
|
+
_CV_INSIDE_TMUX="false"; [ -n "$TMUX" ] && _CV_INSIDE_TMUX="true"
|
|
149
|
+
_CV_IS_TTY="false"; { [ -t 0 ] && [ -t 1 ]; } && _CV_IS_TTY="true"
|
|
150
|
+
cv_telem "wrapper_started" "\"invocation\":\"session\",\"os\":\"$_CV_OS_VER\",\"arch\":\"$_CV_ARCH_VER\",\"claude_version\":\"$_CV_CLAUDE_VER\",\"node_version\":\"$_CV_NODE_VER\",\"tmux_version\":\"$_CV_TMUX_VER\",\"claude_credentials_present\":$_CV_CLAUDE_CREDS,\"inside_tmux\":$_CV_INSIDE_TMUX,\"is_terminal\":$_CV_IS_TTY"
|
|
151
|
+
|
|
152
|
+
# Check authentication before launching. Same dual-path lookup as the
|
|
153
|
+
# auth case above so the hoisted meta-package install location works.
|
|
62
154
|
CORE_CLI="$PLUGIN_DIR/node_modules/@quantiya/codevibe-core/bin/codevibe.js"
|
|
155
|
+
if [ ! -f "$CORE_CLI" ]; then
|
|
156
|
+
CORE_CLI="$PLUGIN_DIR/../codevibe-core/bin/codevibe.js"
|
|
157
|
+
fi
|
|
63
158
|
if [ -f "$CORE_CLI" ]; then
|
|
64
159
|
AUTH_STATUS=$(node "$CORE_CLI" status 2>/dev/null)
|
|
65
160
|
if echo "$AUTH_STATUS" | grep -q "Not authenticated"; then
|
|
@@ -67,6 +162,8 @@ if [ -f "$CORE_CLI" ]; then
|
|
|
67
162
|
echo "⚠️ CodeVibe: Not authenticated."
|
|
68
163
|
echo " Run 'codevibe-claude login' to sign in first."
|
|
69
164
|
echo ""
|
|
165
|
+
cv_failed "codevibe_not_authenticated"
|
|
166
|
+
sleep 1
|
|
70
167
|
exit 1
|
|
71
168
|
fi
|
|
72
169
|
fi
|
|
@@ -83,26 +180,126 @@ log() {
|
|
|
83
180
|
if ! command -v tmux &> /dev/null; then
|
|
84
181
|
echo "Error: tmux is required but not installed."
|
|
85
182
|
echo "Install with: brew install tmux"
|
|
183
|
+
cv_failed "tmux_missing"
|
|
184
|
+
sleep 1
|
|
185
|
+
exit 1
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
# Check if claude CLI is installed.
|
|
189
|
+
# The meta-package wrapper (codevibe/bin/codevibe-claude) does this same
|
|
190
|
+
# check at a higher layer, but a user invoking the plugin wrapper
|
|
191
|
+
# directly (e.g., when developing locally) will hit this fallback. Without
|
|
192
|
+
# it, missing claude becomes an inner-tmux exit-127 which leaves the
|
|
193
|
+
# diagnostic ambiguity that wrapper telemetry is meant to remove.
|
|
194
|
+
if ! command -v claude &> /dev/null; then
|
|
195
|
+
echo "Error: claude CLI is not installed."
|
|
196
|
+
echo "Install Claude Code from https://docs.anthropic.com/claude/docs/claude-code"
|
|
197
|
+
cv_failed "claude_missing"
|
|
198
|
+
sleep 1
|
|
86
199
|
exit 1
|
|
87
200
|
fi
|
|
88
201
|
|
|
202
|
+
# Check if the plugin's MCP server bundle is built. The daemon is
|
|
203
|
+
# launched by Claude Code's SessionStart hook, not by this wrapper, but
|
|
204
|
+
# if dist/server.js is missing the hook will silently fail and "no
|
|
205
|
+
# SessionStart hook fired" will be misattributed to claude itself.
|
|
206
|
+
if [ ! -f "$PLUGIN_DIR/dist/server.js" ]; then
|
|
207
|
+
echo "Error: MCP server bundle not built (missing $PLUGIN_DIR/dist/server.js)."
|
|
208
|
+
echo "Run 'npm run build' in the plugin directory, or reinstall:"
|
|
209
|
+
echo " npm install -g @quantiya/codevibe"
|
|
210
|
+
cv_failed "server_not_built"
|
|
211
|
+
sleep 1
|
|
212
|
+
exit 1
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
# Cleanup function for telemetry — Claude has no server lifecycle in
|
|
216
|
+
# the wrapper (daemon is managed by Claude Code's SessionStart hook),
|
|
217
|
+
# so this trap exists purely to fire wrapper_exited on EXIT and to
|
|
218
|
+
# remove the per-run exit-code file.
|
|
219
|
+
cleanup() {
|
|
220
|
+
local wrapper_exit_code=$?
|
|
221
|
+
|
|
222
|
+
if [ -z "$_CV_EXITED" ]; then
|
|
223
|
+
_CV_EXITED="exited"
|
|
224
|
+
local claude_exit="unknown"
|
|
225
|
+
if [ -f "$_CV_CLAUDE_EXIT_FILE" ]; then
|
|
226
|
+
claude_exit="$(cat "$_CV_CLAUDE_EXIT_FILE" 2>/dev/null | head -c 10 | tr -d '\n\r ')"
|
|
227
|
+
[ -z "$claude_exit" ] && claude_exit="unknown"
|
|
228
|
+
fi
|
|
229
|
+
local lifetime=$(( $(date +%s) - _CV_STARTED_AT ))
|
|
230
|
+
local claude_lifetime=0
|
|
231
|
+
if [ "$_CV_AGENT_STARTED_AT" -gt 0 ] 2>/dev/null; then
|
|
232
|
+
claude_lifetime=$(( $(date +%s) - _CV_AGENT_STARTED_AT ))
|
|
233
|
+
fi
|
|
234
|
+
local hook_fired="false"
|
|
235
|
+
if [ -f "$_CV_MCP_LOG" ]; then
|
|
236
|
+
if tail -n "+$((_CV_MCP_LOG_BASELINE + 1))" "$_CV_MCP_LOG" 2>/dev/null \
|
|
237
|
+
| grep -q "SessionStart" 2>/dev/null; then
|
|
238
|
+
hook_fired="true"
|
|
239
|
+
fi
|
|
240
|
+
fi
|
|
241
|
+
# Outcome priority: SIGINT/SIGTERM beats everything (user intent).
|
|
242
|
+
# Then "we never got far enough to invoke claude" — distinct from
|
|
243
|
+
# "we invoked claude via passthrough but never started a tmux of
|
|
244
|
+
# our own" (the latter is a normal direct-run, not an abort).
|
|
245
|
+
local outcome
|
|
246
|
+
if [ "$wrapper_exit_code" = "130" ] || [ "$wrapper_exit_code" = "143" ]; then
|
|
247
|
+
outcome="interrupted"
|
|
248
|
+
elif [ "$_CV_AGENT_INVOKED" = "false" ]; then
|
|
249
|
+
outcome="pre_invoke_abort"
|
|
250
|
+
elif [ "$claude_exit" != "unknown" ] && [ "$claude_exit" != "0" ]; then
|
|
251
|
+
outcome="error_exit"
|
|
252
|
+
elif [ "$claude_lifetime" -lt 5 ] 2>/dev/null; then
|
|
253
|
+
outcome="early_exit"
|
|
254
|
+
elif [ "$claude_lifetime" -lt 60 ] 2>/dev/null; then
|
|
255
|
+
outcome="clean_short"
|
|
256
|
+
else
|
|
257
|
+
outcome="clean_long"
|
|
258
|
+
fi
|
|
259
|
+
cv_telem "wrapper_exited" "\"exit_code\":$wrapper_exit_code,\"lifetime_seconds\":$lifetime,\"claude_exit_code\":\"$claude_exit\",\"claude_lifetime_seconds\":$claude_lifetime,\"tmux_session_started\":$_CV_TMUX_STARTED,\"agent_invoked\":$_CV_AGENT_INVOKED,\"session_start_hook_fired\":$hook_fired,\"terminal_outcome\":\"$outcome\""
|
|
260
|
+
fi
|
|
261
|
+
|
|
262
|
+
rm -f "$_CV_CLAUDE_EXIT_FILE"
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
trap cleanup EXIT INT TERM
|
|
266
|
+
|
|
89
267
|
# Generate a unique session name
|
|
90
268
|
SESSION_NAME="${TMUX_SESSION_PREFIX}-$$"
|
|
91
269
|
|
|
92
270
|
log "Starting codevibe-claude with session: $SESSION_NAME"
|
|
93
271
|
log "Arguments: $*"
|
|
94
272
|
|
|
95
|
-
# Check if we're already inside tmux
|
|
273
|
+
# Check if we're already inside tmux.
|
|
274
|
+
# We deliberately do NOT `exec` here — running claude as a child process
|
|
275
|
+
# lets the EXIT trap fire after it returns so wrapper_exited still gets
|
|
276
|
+
# emitted on these direct-run paths. Behaviorally identical for the user
|
|
277
|
+
# (claude remains the foreground process for the duration).
|
|
96
278
|
if [ -n "$TMUX" ]; then
|
|
97
279
|
log "Already inside tmux, running claude directly"
|
|
98
|
-
|
|
99
|
-
|
|
280
|
+
_CV_AGENT_INVOKED="true"
|
|
281
|
+
_CV_AGENT_STARTED_AT="$(date +%s)"
|
|
282
|
+
# `|| _CV_RC=$?` is load-bearing: with `set -e`, a non-zero exit
|
|
283
|
+
# from claude would abort the wrapper before we capture the exit
|
|
284
|
+
# code, leaving wrapper_exited with claude_exit_code="unknown". The
|
|
285
|
+
# `||` form catches non-zero without triggering set -e, while exit
|
|
286
|
+
# 0 leaves _CV_RC at its 0 default. printf's `|| true` keeps a
|
|
287
|
+
# disk-full failure from clobbering diagnostics.
|
|
288
|
+
_CV_RC=0
|
|
289
|
+
claude "$@" || _CV_RC=$?
|
|
290
|
+
printf '%s' "$_CV_RC" > "$_CV_CLAUDE_EXIT_FILE" 2>/dev/null || true
|
|
291
|
+
exit "$_CV_RC"
|
|
100
292
|
fi
|
|
101
293
|
|
|
102
|
-
# Check if running in a terminal
|
|
294
|
+
# Check if running in a terminal — same direct-run treatment as above.
|
|
103
295
|
if [ ! -t 0 ] || [ ! -t 1 ]; then
|
|
104
296
|
log "Not running in a terminal, running claude directly"
|
|
105
|
-
|
|
297
|
+
_CV_AGENT_INVOKED="true"
|
|
298
|
+
_CV_AGENT_STARTED_AT="$(date +%s)"
|
|
299
|
+
_CV_RC=0
|
|
300
|
+
claude "$@" || _CV_RC=$?
|
|
301
|
+
printf '%s' "$_CV_RC" > "$_CV_CLAUDE_EXIT_FILE" 2>/dev/null || true
|
|
302
|
+
exit "$_CV_RC"
|
|
106
303
|
fi
|
|
107
304
|
|
|
108
305
|
# Create tmux session and run claude
|
|
@@ -125,7 +322,10 @@ done
|
|
|
125
322
|
# 3. Exits the tmux session when claude exits
|
|
126
323
|
|
|
127
324
|
tmux new-session -d -s "$SESSION_NAME" -x "$(tput cols)" -y "$(tput lines)" \
|
|
128
|
-
"export CODEVIBE_TMUX_SESSION='$SESSION_NAME'; export ENVIRONMENT='$ENVIRONMENT'; $CLAUDE_CMD; exit"
|
|
325
|
+
"export CODEVIBE_TMUX_SESSION='$SESSION_NAME'; export ENVIRONMENT='$ENVIRONMENT'; $CLAUDE_CMD; printf '%s' \"\$?\" > '$_CV_CLAUDE_EXIT_FILE'; exit"
|
|
326
|
+
_CV_TMUX_STARTED="true"
|
|
327
|
+
_CV_AGENT_INVOKED="true"
|
|
328
|
+
_CV_AGENT_STARTED_AT="$(date +%s)"
|
|
129
329
|
|
|
130
330
|
# Enable mouse support for scrolling
|
|
131
331
|
tmux set-option -t "$SESSION_NAME" -g mouse on
|
|
@@ -153,6 +353,9 @@ echo "$SESSION_NAME" > "${CODEVIBE_TMPDIR}/codevibe-claude-tmux-session-$$"
|
|
|
153
353
|
|
|
154
354
|
log "Attaching to tmux session: $SESSION_NAME"
|
|
155
355
|
|
|
156
|
-
# Attach to the session
|
|
157
|
-
#
|
|
158
|
-
|
|
356
|
+
# Attach to the session.
|
|
357
|
+
# Note: we deliberately do NOT `exec` tmux here — letting bash continue
|
|
358
|
+
# means the EXIT trap fires after detach, which is how the wrapper_exited
|
|
359
|
+
# telemetry event gets recorded. The behavioral difference is invisible
|
|
360
|
+
# to the user (tmux attach is still the foreground command).
|
|
361
|
+
tmux attach-session -t "$SESSION_NAME"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quantiya/codevibe-claude-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"description": "Control Claude Code from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"bin": {
|