@quantiya/codevibe-claude-plugin 1.0.6 → 1.0.8
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/hooks/permission-request.sh +63 -81
- package/hooks/stop.sh +99 -106
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codevibe-claude",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Sync Claude Code sessions with iOS mobile app via AWS backend. Control Claude Code from your iPhone with real-time bidirectional synchronization.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "CodeVibe Team"
|
|
@@ -39,92 +39,74 @@ SENT_UUIDS_FILE="${CODEVIBE_TMPDIR}/codevibe-claude-sent-uuids-${SESSION_ID}.txt
|
|
|
39
39
|
if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
|
|
40
40
|
log "DEBUG" "Reading transcript for assistant responses: $TRANSCRIPT_PATH"
|
|
41
41
|
|
|
42
|
-
#
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
# Track assistant messages in current turn (chained by parentUuid)
|
|
49
|
-
declare -a CURRENT_TURN_UUIDS
|
|
50
|
-
CURRENT_TURN_UUIDS=("$LAST_USER_UUID")
|
|
51
|
-
|
|
52
|
-
# First pass: build the complete UUID chain including both assistant and user (tool_result) messages
|
|
53
|
-
# This is needed because the chain goes: user_prompt -> assistant -> user(tool_result) -> assistant -> ...
|
|
54
|
-
while IFS= read -r line; do
|
|
55
|
-
PARENT_UUID=$(echo "$line" | jq -r '.parentUuid // empty')
|
|
56
|
-
MESSAGE_UUID=$(echo "$line" | jq -r '.uuid // empty')
|
|
57
|
-
|
|
58
|
-
# Check if parent is in current turn - if so, add this UUID to the chain
|
|
59
|
-
for turn_uuid in "${CURRENT_TURN_UUIDS[@]}"; do
|
|
60
|
-
if [ "$PARENT_UUID" = "$turn_uuid" ]; then
|
|
61
|
-
CURRENT_TURN_UUIDS+=("$MESSAGE_UUID")
|
|
62
|
-
break
|
|
63
|
-
fi
|
|
64
|
-
done
|
|
65
|
-
done < "$TRANSCRIPT_PATH"
|
|
66
|
-
|
|
67
|
-
log "DEBUG" "Built UUID chain with ${#CURRENT_TURN_UUIDS[@]} entries"
|
|
68
|
-
|
|
69
|
-
# Second pass: extract and send assistant text messages that are in the chain
|
|
70
|
-
while IFS= read -r line; do
|
|
71
|
-
MESSAGE_UUID=$(echo "$line" | jq -r '.uuid // empty')
|
|
72
|
-
|
|
73
|
-
# Check if this message is in our turn chain
|
|
74
|
-
IS_CURRENT_TURN=false
|
|
75
|
-
for turn_uuid in "${CURRENT_TURN_UUIDS[@]}"; do
|
|
76
|
-
if [ "$MESSAGE_UUID" = "$turn_uuid" ]; then
|
|
77
|
-
IS_CURRENT_TURN=true
|
|
78
|
-
break
|
|
79
|
-
fi
|
|
80
|
-
done
|
|
81
|
-
|
|
82
|
-
if [ "$IS_CURRENT_TURN" = false ]; then
|
|
83
|
-
continue
|
|
84
|
-
fi
|
|
85
|
-
|
|
86
|
-
# Skip if this message UUID was already sent
|
|
87
|
-
if [ -f "$SENT_UUIDS_FILE" ] && grep -q "^${MESSAGE_UUID}$" "$SENT_UUIDS_FILE"; then
|
|
88
|
-
log "DEBUG" "Skipping already sent message UUID: $MESSAGE_UUID"
|
|
89
|
-
continue
|
|
90
|
-
fi
|
|
91
|
-
|
|
92
|
-
MESSAGE_CONTENT=$(echo "$line" | jq -r '.message.content // empty')
|
|
42
|
+
# Load already-sent UUIDs for filtering
|
|
43
|
+
SENT_UUIDS_CONTENT=""
|
|
44
|
+
if [ -f "$SENT_UUIDS_FILE" ]; then
|
|
45
|
+
SENT_UUIDS_CONTENT=$(cat "$SENT_UUIDS_FILE")
|
|
46
|
+
fi
|
|
93
47
|
|
|
94
|
-
|
|
48
|
+
# Single jq invocation: find last user UUID, build chain, extract unsent assistant text
|
|
49
|
+
# This replaces two slow bash while-loops that spawned jq per line (O(n) jq processes → 1)
|
|
50
|
+
ASSISTANT_MESSAGES=$(jq -r --slurp --arg sent "$SENT_UUIDS_CONTENT" '
|
|
51
|
+
# Find last user prompt UUID
|
|
52
|
+
(map(select(.type == "user")) | last | .uuid // empty) as $lastUserUuid |
|
|
53
|
+
if ($lastUserUuid | length) == 0 then empty
|
|
54
|
+
else
|
|
55
|
+
# Build UUID chain from last user prompt
|
|
56
|
+
reduce .[] as $msg (
|
|
57
|
+
[$lastUserUuid];
|
|
58
|
+
if (. | any(. == ($msg.parentUuid // ""))) then
|
|
59
|
+
. + [($msg.uuid // "")]
|
|
60
|
+
else . end
|
|
61
|
+
) as $chain |
|
|
62
|
+
# Parse sent UUIDs
|
|
63
|
+
($sent | split("\n") | map(select(. != ""))) as $sentList |
|
|
64
|
+
# Filter: assistant messages in chain, not sent, with text content
|
|
65
|
+
.[] |
|
|
66
|
+
select(.type == "assistant") |
|
|
67
|
+
select(.uuid as $u | $chain | any(. == $u)) |
|
|
68
|
+
select(.uuid as $u | $sentList | any(. == $u) | not) |
|
|
69
|
+
{
|
|
70
|
+
uuid: .uuid,
|
|
71
|
+
text: ([.message.content[]? | select(.type == "text") | .text // empty] | join(" "))
|
|
72
|
+
} |
|
|
73
|
+
select(.text | length > 0) |
|
|
74
|
+
"\(.uuid)\t\(.text | @base64)"
|
|
75
|
+
end
|
|
76
|
+
' "$TRANSCRIPT_PATH" 2>/dev/null)
|
|
77
|
+
|
|
78
|
+
if [ -n "$ASSISTANT_MESSAGES" ]; then
|
|
79
|
+
while IFS=$'\t' read -r MSG_UUID MSG_TEXT_B64; do
|
|
80
|
+
if [ -z "$MSG_UUID" ] || [ -z "$MSG_TEXT_B64" ]; then
|
|
95
81
|
continue
|
|
96
82
|
fi
|
|
97
83
|
|
|
98
|
-
#
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
echo "$MESSAGE_UUID" >> "$SENT_UUIDS_FILE"
|
|
123
|
-
else
|
|
124
|
-
log "ERROR" "Failed to send assistant response from PermissionRequest hook"
|
|
125
|
-
fi
|
|
84
|
+
# Decode base64-encoded text content (preserves newlines in markdown)
|
|
85
|
+
MSG_TEXT=$(echo "$MSG_TEXT_B64" | base64 -d 2>/dev/null || echo "$MSG_TEXT_B64")
|
|
86
|
+
|
|
87
|
+
ASSISTANT_PAYLOAD=$(jq -n \
|
|
88
|
+
--arg session_id "$SESSION_ID" \
|
|
89
|
+
--arg content "$MSG_TEXT" \
|
|
90
|
+
--arg hook_event_name "PermissionRequest" \
|
|
91
|
+
--arg type "ASSISTANT_RESPONSE" \
|
|
92
|
+
'{
|
|
93
|
+
session_id: $session_id,
|
|
94
|
+
hook_event_name: $hook_event_name,
|
|
95
|
+
type: $type,
|
|
96
|
+
content: $content
|
|
97
|
+
}')
|
|
98
|
+
|
|
99
|
+
log "DEBUG" "Sending assistant response from PermissionRequest: ${MSG_TEXT:0:100}..."
|
|
100
|
+
|
|
101
|
+
send_to_mcp "event" "$ASSISTANT_PAYLOAD" "$SESSION_ID"
|
|
102
|
+
|
|
103
|
+
if [ $? -eq 0 ]; then
|
|
104
|
+
log "INFO" "Assistant response sent successfully from PermissionRequest hook"
|
|
105
|
+
echo "$MSG_UUID" >> "$SENT_UUIDS_FILE"
|
|
106
|
+
else
|
|
107
|
+
log "ERROR" "Failed to send assistant response from PermissionRequest hook"
|
|
126
108
|
fi
|
|
127
|
-
done
|
|
109
|
+
done <<< "$ASSISTANT_MESSAGES"
|
|
128
110
|
fi
|
|
129
111
|
else
|
|
130
112
|
log "WARN" "Transcript path not found or file doesn't exist: $TRANSCRIPT_PATH"
|
package/hooks/stop.sh
CHANGED
|
@@ -31,123 +31,116 @@ fi
|
|
|
31
31
|
log "DEBUG" "Reading transcript: $TRANSCRIPT_PATH"
|
|
32
32
|
|
|
33
33
|
# Parse transcript and extract events since last user prompt
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
# 2. Find last user prompt (type=user with prompt content)
|
|
37
|
-
# 3. Extract all assistant messages after that prompt
|
|
38
|
-
# 4. Send each as separate event to MCP server
|
|
39
|
-
|
|
40
|
-
# Get the last user prompt UUID to know where assistant messages start
|
|
41
|
-
LAST_USER_UUID=$(grep '"type":"user"' "$TRANSCRIPT_PATH" | tail -1 | jq -r '.uuid // empty')
|
|
42
|
-
|
|
43
|
-
if [ -z "$LAST_USER_UUID" ]; then
|
|
44
|
-
log "WARN" "No user prompt found in transcript"
|
|
45
|
-
exit 0
|
|
46
|
-
fi
|
|
47
|
-
|
|
48
|
-
log "DEBUG" "Last user prompt UUID: $LAST_USER_UUID"
|
|
49
|
-
|
|
50
|
-
# Extract all assistant messages after the last user prompt
|
|
51
|
-
# We'll read the file and process assistant messages
|
|
34
|
+
# Single jq --slurp invocation: finds last user UUID, builds parent chain,
|
|
35
|
+
# extracts assistant text + tool_use events. ~200x faster than per-line bash+jq loops.
|
|
52
36
|
EVENTS_SENT=0
|
|
53
37
|
|
|
54
38
|
# Track sent message UUIDs to avoid duplicates (shared with PermissionRequest hook)
|
|
55
39
|
SENT_UUIDS_FILE="${CODEVIBE_TMPDIR}/codevibe-claude-sent-uuids-${SESSION_ID}.txt"
|
|
56
40
|
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
# First pass: build the complete UUID chain including both assistant and user (tool_result) messages
|
|
63
|
-
# This is needed because the chain goes: user_prompt -> assistant -> user(tool_result) -> assistant -> ...
|
|
64
|
-
while IFS= read -r line; do
|
|
65
|
-
PARENT_UUID=$(echo "$line" | jq -r '.parentUuid // empty')
|
|
66
|
-
MESSAGE_UUID=$(echo "$line" | jq -r '.uuid // empty')
|
|
67
|
-
|
|
68
|
-
# Check if parent is in current turn - if so, add this UUID to the chain
|
|
69
|
-
for turn_uuid in "${CURRENT_TURN_UUIDS[@]}"; do
|
|
70
|
-
if [ "$PARENT_UUID" = "$turn_uuid" ]; then
|
|
71
|
-
CURRENT_TURN_UUIDS+=("$MESSAGE_UUID")
|
|
72
|
-
break
|
|
73
|
-
fi
|
|
74
|
-
done
|
|
75
|
-
done < "$TRANSCRIPT_PATH"
|
|
76
|
-
|
|
77
|
-
log "DEBUG" "Built UUID chain with ${#CURRENT_TURN_UUIDS[@]} entries"
|
|
78
|
-
|
|
79
|
-
# Second pass: extract and send assistant messages that are in the chain
|
|
80
|
-
while IFS= read -r line; do
|
|
81
|
-
MESSAGE_UUID=$(echo "$line" | jq -r '.uuid // empty')
|
|
41
|
+
# Load already-sent UUIDs for filtering
|
|
42
|
+
SENT_UUIDS_CONTENT=""
|
|
43
|
+
if [ -f "$SENT_UUIDS_FILE" ]; then
|
|
44
|
+
SENT_UUIDS_CONTENT=$(cat "$SENT_UUIDS_FILE")
|
|
45
|
+
fi
|
|
82
46
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
47
|
+
# Single jq invocation: find last user UUID, build chain, extract assistant messages
|
|
48
|
+
# This replaces two slow bash while-loops that spawned jq per line (O(n) jq processes → 1)
|
|
49
|
+
# Output format: tab-separated lines: uuid\ttype\tcontent
|
|
50
|
+
# type is "text" for base64-encoded assistant text, "tool_use" for JSON tool uses
|
|
51
|
+
# Text is base64-encoded because it may contain newlines which break line-by-line read
|
|
52
|
+
TRANSCRIPT_EVENTS=$(jq -r --slurp --arg sent "$SENT_UUIDS_CONTENT" '
|
|
53
|
+
# Find last user prompt UUID
|
|
54
|
+
(map(select(.type == "user")) | last | .uuid // empty) as $lastUserUuid |
|
|
55
|
+
if ($lastUserUuid | length) == 0 then empty
|
|
56
|
+
else
|
|
57
|
+
# Build UUID chain from last user prompt
|
|
58
|
+
reduce .[] as $msg (
|
|
59
|
+
[$lastUserUuid];
|
|
60
|
+
if (. | any(. == ($msg.parentUuid // ""))) then
|
|
61
|
+
. + [($msg.uuid // "")]
|
|
62
|
+
else . end
|
|
63
|
+
) as $chain |
|
|
64
|
+
# Parse sent UUIDs
|
|
65
|
+
($sent | split("\n") | map(select(. != ""))) as $sentList |
|
|
66
|
+
# Filter: assistant messages in chain
|
|
67
|
+
.[] |
|
|
68
|
+
select(.type == "assistant") |
|
|
69
|
+
select(.uuid as $u | $chain | any(. == $u)) |
|
|
70
|
+
. as $msg |
|
|
71
|
+
# Emit text content (base64-encoded to preserve newlines)
|
|
72
|
+
(
|
|
73
|
+
($msg.uuid) as $uuid |
|
|
74
|
+
($sentList | any(. == $uuid)) as $alreadySent |
|
|
75
|
+
if $alreadySent then
|
|
76
|
+
"\($uuid)\talready_sent\t"
|
|
77
|
+
else
|
|
78
|
+
([$msg.message.content[]? | select(.type == "text") | .text // empty] | join(" ")) as $text |
|
|
79
|
+
if ($text | length) > 0 then
|
|
80
|
+
"\($uuid)\ttext\t\($text | @base64)"
|
|
81
|
+
else empty end
|
|
82
|
+
end
|
|
83
|
+
),
|
|
84
|
+
# Emit tool_use content as JSON (tojson escapes newlines, safe for line read)
|
|
85
|
+
(
|
|
86
|
+
$msg.message.content[]? | select(.type == "tool_use") |
|
|
87
|
+
"\($msg.uuid)\ttool_use\t\(. | tojson | @base64)"
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
' "$TRANSCRIPT_PATH" 2>/dev/null)
|
|
91
|
+
|
|
92
|
+
log "DEBUG" "Transcript parsing complete"
|
|
93
|
+
|
|
94
|
+
if [ -n "$TRANSCRIPT_EVENTS" ]; then
|
|
95
|
+
while IFS=$'\t' read -r MSG_UUID MSG_TYPE MSG_CONTENT; do
|
|
96
|
+
if [ -z "$MSG_UUID" ]; then
|
|
97
|
+
continue
|
|
89
98
|
fi
|
|
90
|
-
done
|
|
91
|
-
|
|
92
|
-
if [ "$IS_CURRENT_TURN" = false ]; then
|
|
93
|
-
continue
|
|
94
|
-
fi
|
|
95
99
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
fi
|
|
101
|
-
|
|
102
|
-
MESSAGE_CONTENT=$(echo "$line" | jq -r '.message.content // empty')
|
|
103
|
-
|
|
104
|
-
if [ -z "$MESSAGE_CONTENT" ] || [ "$MESSAGE_CONTENT" = "null" ]; then
|
|
105
|
-
continue
|
|
106
|
-
fi
|
|
107
|
-
|
|
108
|
-
# Extract text content from assistant message
|
|
109
|
-
TEXT_CONTENT=$(echo "$line" | jq -r '.message.content[] | select(.type == "text") | .text // empty' | tr '\n' ' ')
|
|
110
|
-
|
|
111
|
-
if [ -n "$TEXT_CONTENT" ] && [ "$TEXT_CONTENT" != "null" ]; then
|
|
112
|
-
# Create ASSISTANT_RESPONSE event (SESSION_ID already extracted at top)
|
|
113
|
-
EVENT_PAYLOAD=$(jq -n \
|
|
114
|
-
--arg session_id "$SESSION_ID" \
|
|
115
|
-
--arg content "$TEXT_CONTENT" \
|
|
116
|
-
--arg hook_event_name "Stop" \
|
|
117
|
-
--arg type "ASSISTANT_RESPONSE" \
|
|
118
|
-
'{
|
|
119
|
-
session_id: $session_id,
|
|
120
|
-
hook_event_name: $hook_event_name,
|
|
121
|
-
type: $type,
|
|
122
|
-
content: $content
|
|
123
|
-
}')
|
|
124
|
-
|
|
125
|
-
log "DEBUG" "Sending assistant response: ${TEXT_CONTENT:0:100}..."
|
|
126
|
-
|
|
127
|
-
send_to_mcp "event" "$EVENT_PAYLOAD" "$SESSION_ID"
|
|
128
|
-
|
|
129
|
-
if [ $? -eq 0 ]; then
|
|
130
|
-
EVENTS_SENT=$((EVENTS_SENT + 1))
|
|
131
|
-
log "INFO" "Assistant response sent successfully"
|
|
132
|
-
# Track that this message UUID was sent
|
|
133
|
-
echo "$MESSAGE_UUID" >> "$SENT_UUIDS_FILE"
|
|
134
|
-
else
|
|
135
|
-
log "ERROR" "Failed to send assistant response"
|
|
100
|
+
if [ "$MSG_TYPE" = "already_sent" ]; then
|
|
101
|
+
log "DEBUG" "Skipping already sent message UUID: $MSG_UUID"
|
|
102
|
+
EVENTS_SENT=$((EVENTS_SENT + 1)) # Count as sent to prevent fallback duplicate
|
|
103
|
+
continue
|
|
136
104
|
fi
|
|
137
|
-
fi
|
|
138
105
|
|
|
139
|
-
|
|
140
|
-
|
|
106
|
+
if [ "$MSG_TYPE" = "text" ] && [ -n "$MSG_CONTENT" ]; then
|
|
107
|
+
# Decode base64-encoded text content
|
|
108
|
+
DECODED_CONTENT=$(echo "$MSG_CONTENT" | base64 -d 2>/dev/null || echo "$MSG_CONTENT")
|
|
109
|
+
|
|
110
|
+
EVENT_PAYLOAD=$(jq -n \
|
|
111
|
+
--arg session_id "$SESSION_ID" \
|
|
112
|
+
--arg content "$DECODED_CONTENT" \
|
|
113
|
+
--arg hook_event_name "Stop" \
|
|
114
|
+
--arg type "ASSISTANT_RESPONSE" \
|
|
115
|
+
'{
|
|
116
|
+
session_id: $session_id,
|
|
117
|
+
hook_event_name: $hook_event_name,
|
|
118
|
+
type: $type,
|
|
119
|
+
content: $content
|
|
120
|
+
}')
|
|
121
|
+
|
|
122
|
+
log "DEBUG" "Sending assistant response: ${DECODED_CONTENT:0:100}..."
|
|
123
|
+
|
|
124
|
+
send_to_mcp "event" "$EVENT_PAYLOAD" "$SESSION_ID"
|
|
125
|
+
|
|
126
|
+
if [ $? -eq 0 ]; then
|
|
127
|
+
EVENTS_SENT=$((EVENTS_SENT + 1))
|
|
128
|
+
log "INFO" "Assistant response sent successfully"
|
|
129
|
+
echo "$MSG_UUID" >> "$SENT_UUIDS_FILE"
|
|
130
|
+
else
|
|
131
|
+
log "ERROR" "Failed to send assistant response"
|
|
132
|
+
fi
|
|
133
|
+
fi
|
|
141
134
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
135
|
+
if [ "$MSG_TYPE" = "tool_use" ] && [ -n "$MSG_CONTENT" ]; then
|
|
136
|
+
# Decode base64-encoded tool_use JSON
|
|
137
|
+
DECODED_TOOL=$(echo "$MSG_CONTENT" | base64 -d 2>/dev/null || echo "$MSG_CONTENT")
|
|
138
|
+
TOOL_NAME=$(echo "$DECODED_TOOL" | jq -r '.name // empty')
|
|
139
|
+
TOOL_INPUT=$(echo "$DECODED_TOOL" | jq -c '.input // {}')
|
|
146
140
|
|
|
147
141
|
if [ -n "$TOOL_NAME" ]; then
|
|
148
142
|
# Check if this is an interactive prompt (AskUserQuestion)
|
|
149
143
|
if [ "$TOOL_NAME" = "AskUserQuestion" ]; then
|
|
150
|
-
# Extract question text from input
|
|
151
144
|
QUESTION_TEXT=$(echo "$TOOL_INPUT" | jq -r '.questions[0].question // empty')
|
|
152
145
|
|
|
153
146
|
if [ -n "$QUESTION_TEXT" ]; then
|
|
@@ -181,7 +174,7 @@ while IFS= read -r line; do
|
|
|
181
174
|
fi
|
|
182
175
|
fi
|
|
183
176
|
|
|
184
|
-
# Send regular TOOL_USE event for all tools
|
|
177
|
+
# Send regular TOOL_USE event for all tools
|
|
185
178
|
TOOL_EVENT_PAYLOAD=$(jq -n \
|
|
186
179
|
--arg session_id "$SESSION_ID" \
|
|
187
180
|
--arg hook_event_name "Stop" \
|
|
@@ -209,10 +202,10 @@ while IFS= read -r line; do
|
|
|
209
202
|
log "ERROR" "Failed to send tool use event"
|
|
210
203
|
fi
|
|
211
204
|
fi
|
|
212
|
-
|
|
213
|
-
fi
|
|
205
|
+
fi
|
|
214
206
|
|
|
215
|
-
done
|
|
207
|
+
done <<< "$TRANSCRIPT_EVENTS"
|
|
208
|
+
fi
|
|
216
209
|
|
|
217
210
|
# Fallback: if no events were extracted from transcript but last_assistant_message is available,
|
|
218
211
|
# send it directly. This handles the race condition where the Stop hook fires before the
|
package/package.json
CHANGED