@pragma-sh/claude-code-plugin 0.1.0-alpha.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pragma-plugin.mjs +3 -3
- package/hooks/report.sh +91 -8
- package/package.json +8 -3
- package/pragma-plugin.json +5 -1
- package/src/pragma-plugin.ts +2 -2
package/dist/pragma-plugin.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// ../plugin/dist/shared/chunk-
|
|
2
|
-
var PLUGIN_API_VERSION = "
|
|
1
|
+
// ../plugin/dist/shared/chunk-jypnsjrp.js
|
|
2
|
+
var PLUGIN_API_VERSION = "1.0.0";
|
|
3
3
|
|
|
4
|
-
// ../plugin/dist/shared/chunk-
|
|
4
|
+
// ../plugin/dist/shared/chunk-jx8c1n1n.js
|
|
5
5
|
function defineAgent(input) {
|
|
6
6
|
return input;
|
|
7
7
|
}
|
package/hooks/report.sh
CHANGED
|
@@ -50,6 +50,8 @@ children_dir="${state_dir}/pragma-cli-${agent}-${tab}.subagents"
|
|
|
50
50
|
# marker-guarded report for the rest of the turn. This file lets `cleared` tell
|
|
51
51
|
# a stale clear from a legitimate one.
|
|
52
52
|
turn_session_file="${state_dir}/pragma-cli-${agent}-${tab}.turn-session"
|
|
53
|
+
assistant_cursor_file="${state_dir}/pragma-cli-${agent}-${tab}.assistant-cursor"
|
|
54
|
+
assistant_hash_file="${state_dir}/pragma-cli-${agent}-${tab}.assistant-hash"
|
|
53
55
|
|
|
54
56
|
# Poll cadence and absolute lifetime backstop (overridable for tests). The
|
|
55
57
|
# backstop guarantees a watcher can't outlive its session forever if the session
|
|
@@ -78,7 +80,7 @@ report_checked() {
|
|
|
78
80
|
|
|
79
81
|
# Reports a coarse rich message. Hook payloads are intentionally not parsed here
|
|
80
82
|
# beyond existing transcript handling; this keeps hooks fail-open and portable.
|
|
81
|
-
# AgentMessage.ts is milliseconds since Unix epoch (see @pragma/constants).
|
|
83
|
+
# AgentMessage.ts is milliseconds since Unix epoch (see @pragma-sh/constants).
|
|
82
84
|
# `date +%s` is seconds — multiply so chat clients that stamp local input with
|
|
83
85
|
# Date.now() don't sort every agent bubble above the user's messages.
|
|
84
86
|
message_ts_ms() {
|
|
@@ -185,6 +187,81 @@ print(json.dumps({
|
|
|
185
187
|
"$pragma_cli" agent message --agent "$agent" --payload "$payload" >/dev/null 2>&1 || true
|
|
186
188
|
}
|
|
187
189
|
|
|
190
|
+
# Reports assistant text once per turn. Stop payloads often repeat the newest
|
|
191
|
+
# transcript segment, so retain its checksum to avoid duplicate chat bubbles.
|
|
192
|
+
assistant_message_once() {
|
|
193
|
+
text="$1"
|
|
194
|
+
[ -n "$text" ] || return 0
|
|
195
|
+
hash=$(printf '%s' "$text" | cksum | tr -d '[:space:]')
|
|
196
|
+
[ "$(cat "$assistant_hash_file" 2>/dev/null)" = "$hash" ] && return 0
|
|
197
|
+
content_message assistant "$text"
|
|
198
|
+
printf '%s' "$hash" >"$assistant_hash_file"
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
assistant_text_count() {
|
|
202
|
+
tp="$1"
|
|
203
|
+
[ -n "$py3" ] && [ -n "$tp" ] && [ -f "$tp" ] || {
|
|
204
|
+
printf '0'
|
|
205
|
+
return 0
|
|
206
|
+
}
|
|
207
|
+
"$py3" -c '
|
|
208
|
+
import json, sys
|
|
209
|
+
count = 0
|
|
210
|
+
for line in open(sys.argv[1], encoding="utf-8", errors="replace"):
|
|
211
|
+
try:
|
|
212
|
+
obj = json.loads(line)
|
|
213
|
+
except Exception:
|
|
214
|
+
continue
|
|
215
|
+
if obj.get("type") != "assistant":
|
|
216
|
+
continue
|
|
217
|
+
content = (obj.get("message") or {}).get("content") or []
|
|
218
|
+
if any(isinstance(c, dict) and c.get("type") == "text" and c.get("text") for c in content):
|
|
219
|
+
count += 1
|
|
220
|
+
print(count)
|
|
221
|
+
' "$tp" 2>/dev/null
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
# Prints base64-encoded assistant text records after a text-record cursor.
|
|
225
|
+
assistant_texts_since() {
|
|
226
|
+
tp="$1"
|
|
227
|
+
start="$2"
|
|
228
|
+
[ -n "$py3" ] && [ -n "$tp" ] && [ -f "$tp" ] || return 0
|
|
229
|
+
"$py3" -c '
|
|
230
|
+
import base64, json, sys
|
|
231
|
+
start = int(sys.argv[2])
|
|
232
|
+
index = 0
|
|
233
|
+
for line in open(sys.argv[1], encoding="utf-8", errors="replace"):
|
|
234
|
+
try:
|
|
235
|
+
obj = json.loads(line)
|
|
236
|
+
except Exception:
|
|
237
|
+
continue
|
|
238
|
+
if obj.get("type") != "assistant":
|
|
239
|
+
continue
|
|
240
|
+
content = (obj.get("message") or {}).get("content") or []
|
|
241
|
+
parts = [c.get("text", "") for c in content if isinstance(c, dict) and c.get("type") == "text"]
|
|
242
|
+
text = "\n".join(p for p in parts if p)
|
|
243
|
+
if not text:
|
|
244
|
+
continue
|
|
245
|
+
if index >= start:
|
|
246
|
+
print(base64.b64encode(text.encode()).decode())
|
|
247
|
+
index += 1
|
|
248
|
+
' "$tp" "$start" 2>/dev/null
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
# Streams newly completed transcript segments. Cursor is initialized when turn
|
|
252
|
+
# starts, excluding every assistant record from earlier turns.
|
|
253
|
+
sync_assistant_transcript() {
|
|
254
|
+
tp="$1"
|
|
255
|
+
[ -n "$py3" ] && [ -n "$tp" ] && [ -f "$tp" ] || return 0
|
|
256
|
+
cursor=$(cat "$assistant_cursor_file" 2>/dev/null)
|
|
257
|
+
[ -n "$cursor" ] || cursor=0
|
|
258
|
+
for encoded in $(assistant_texts_since "$tp" "$cursor"); do
|
|
259
|
+
text=$("$py3" -c 'import base64,sys; print(base64.b64decode(sys.argv[1]).decode())' "$encoded" 2>/dev/null)
|
|
260
|
+
assistant_message_once "$text"
|
|
261
|
+
done
|
|
262
|
+
assistant_text_count "$tp" >"$assistant_cursor_file"
|
|
263
|
+
}
|
|
264
|
+
|
|
188
265
|
# Prints the newest assistant text in a Claude Code transcript JSONL — the
|
|
189
266
|
# reply the turn that just stopped produced. Empty output when unavailable.
|
|
190
267
|
last_assistant_text() {
|
|
@@ -626,7 +703,7 @@ run_watcher() {
|
|
|
626
703
|
# Re-check the token right before acting so we never clear a turn that
|
|
627
704
|
# started in the gap between the poll and now.
|
|
628
705
|
[ "$(cat "$marker" 2>/dev/null)" = "$token" ] || exit 0
|
|
629
|
-
rm -f "$marker" "$turn_session_file"
|
|
706
|
+
rm -f "$marker" "$turn_session_file" "$assistant_cursor_file" "$assistant_hash_file"
|
|
630
707
|
report cleared
|
|
631
708
|
exit 0
|
|
632
709
|
fi
|
|
@@ -710,8 +787,10 @@ case "${1:-}" in
|
|
|
710
787
|
;;
|
|
711
788
|
esac
|
|
712
789
|
stop_watcher
|
|
790
|
+
rm -f "$assistant_cursor_file" "$assistant_hash_file"
|
|
713
791
|
tp="$(transcript_path "$input")"
|
|
714
792
|
if [ -n "$tp" ]; then
|
|
793
|
+
assistant_text_count "$tp" >"$assistant_cursor_file"
|
|
715
794
|
# Pin the watcher to where the transcript stands *now* so a prior turn's
|
|
716
795
|
# interrupt marker (already in the file) can't be mistaken for this turn's
|
|
717
796
|
# cancel while Claude is still thinking and has appended nothing yet.
|
|
@@ -728,7 +807,7 @@ case "${1:-}" in
|
|
|
728
807
|
tp="$(transcript_path "$input")"
|
|
729
808
|
if turn_interrupted "$tp"; then
|
|
730
809
|
stop_watcher
|
|
731
|
-
rm -f "$marker" "$turn_session_file"
|
|
810
|
+
rm -f "$marker" "$turn_session_file" "$assistant_cursor_file" "$assistant_hash_file"
|
|
732
811
|
report cleared
|
|
733
812
|
message system "Claude Code turn interrupted"
|
|
734
813
|
elif has_running_subagents "$input" || has_tracked_subagents; then
|
|
@@ -739,10 +818,11 @@ case "${1:-}" in
|
|
|
739
818
|
# is still detected, and surface the parent's interim reply.
|
|
740
819
|
[ -f "$marker" ] || printf '%s' "$$-$(date +%s)" >"$marker"
|
|
741
820
|
report started
|
|
821
|
+
sync_assistant_transcript "$tp"
|
|
742
822
|
reply="$(json_field last_assistant_message "$input")"
|
|
743
823
|
[ -n "$reply" ] || reply="$(last_assistant_text "$tp")"
|
|
744
824
|
if [ -n "$reply" ]; then
|
|
745
|
-
|
|
825
|
+
assistant_message_once "$reply"
|
|
746
826
|
else
|
|
747
827
|
message assistant "Claude Code is waiting on subagents"
|
|
748
828
|
fi
|
|
@@ -751,17 +831,19 @@ case "${1:-}" in
|
|
|
751
831
|
# green "done" dot.
|
|
752
832
|
stop_watcher
|
|
753
833
|
rm -f "$marker" "$turn_session_file"
|
|
754
|
-
report stopped
|
|
755
834
|
# Stop carries the completed reply directly on current Claude builds.
|
|
756
835
|
# Prefer it over rereading the transcript, then retain transcript support
|
|
757
836
|
# for older builds that omit the field.
|
|
837
|
+
sync_assistant_transcript "$tp"
|
|
758
838
|
reply="$(json_field last_assistant_message "$input")"
|
|
759
839
|
[ -n "$reply" ] || reply="$(last_assistant_text "$tp")"
|
|
760
840
|
if [ -n "$reply" ]; then
|
|
761
|
-
|
|
841
|
+
assistant_message_once "$reply"
|
|
762
842
|
else
|
|
763
843
|
message assistant "Claude Code turn completed"
|
|
764
844
|
fi
|
|
845
|
+
report stopped
|
|
846
|
+
rm -f "$assistant_cursor_file" "$assistant_hash_file"
|
|
765
847
|
fi
|
|
766
848
|
;;
|
|
767
849
|
cleared)
|
|
@@ -778,7 +860,7 @@ case "${1:-}" in
|
|
|
778
860
|
exit 0
|
|
779
861
|
fi
|
|
780
862
|
stop_watcher
|
|
781
|
-
rm -f "$marker" "$turn_session_file"
|
|
863
|
+
rm -f "$marker" "$turn_session_file" "$assistant_cursor_file" "$assistant_hash_file"
|
|
782
864
|
clear_subagents
|
|
783
865
|
if [ "$hook_event_name" = "SessionEnd" ]; then
|
|
784
866
|
rm -f "$session_file"
|
|
@@ -796,6 +878,7 @@ case "${1:-}" in
|
|
|
796
878
|
# marker so a stray PostToolUse outside a turn can't flash a phantom "running".
|
|
797
879
|
if [ -f "$marker" ]; then
|
|
798
880
|
report started
|
|
881
|
+
sync_assistant_transcript "$(transcript_path "$input")"
|
|
799
882
|
message tool "Claude Code tool finished"
|
|
800
883
|
fi
|
|
801
884
|
;;
|
|
@@ -863,7 +946,7 @@ case "${1:-}" in
|
|
|
863
946
|
# Claude Code build emits it, a lingering marker still clears the turn.)
|
|
864
947
|
if [ -f "$marker" ]; then
|
|
865
948
|
stop_watcher
|
|
866
|
-
rm -f "$marker" "$turn_session_file"
|
|
949
|
+
rm -f "$marker" "$turn_session_file" "$assistant_cursor_file" "$assistant_hash_file"
|
|
867
950
|
report cleared
|
|
868
951
|
message system "Claude Code turn cleared"
|
|
869
952
|
fi
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pragma-sh/claude-code-plugin",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Claude Code plugin that reports agent status to Pragma automatically via shell hooks.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/pragma-sh/pragma.git",
|
|
9
|
+
"directory": "packages/claude-code-plugin"
|
|
10
|
+
},
|
|
6
11
|
"files": [
|
|
7
12
|
".claude-plugin",
|
|
8
13
|
"assets",
|
|
@@ -27,8 +32,8 @@
|
|
|
27
32
|
"lint": "oxlint ."
|
|
28
33
|
},
|
|
29
34
|
"devDependencies": {
|
|
30
|
-
"@pragma/plugin": "workspace:*",
|
|
31
|
-
"@pragma/watcher-kit": "workspace:*",
|
|
35
|
+
"@pragma-sh/plugin": "workspace:*",
|
|
36
|
+
"@pragma-sh/watcher-kit": "workspace:*",
|
|
32
37
|
"@types/node": "^24.12.3",
|
|
33
38
|
"bunup": "^0.16.32",
|
|
34
39
|
"typescript": "^6.0.3",
|
package/pragma-plugin.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/pragma-sh/pragma/main/packages/plugin-registry/pragma-plugin.schema.json",
|
|
3
3
|
"name": "Claude Code",
|
|
4
4
|
"description": "Launch Claude Code in Pragma with live status, questions, usage limits, and session naming.",
|
|
5
|
+
"longDescription": "Launch Claude Code from the Pragma launcher and the integration reports live status, questions, approvals, usage limits, and session naming back to the app. Status lands on the tab, the sidebar, and agent alerts, so several Claude Code sessions stay readable side by side across worktrees.",
|
|
5
6
|
"categories": ["agent-plugin"],
|
|
6
7
|
"images": [
|
|
7
8
|
{
|
|
@@ -9,6 +10,9 @@
|
|
|
9
10
|
"alt": "Claude Code logo"
|
|
10
11
|
}
|
|
11
12
|
],
|
|
12
|
-
"install": {
|
|
13
|
+
"install": {
|
|
14
|
+
"command": "sh",
|
|
15
|
+
"args": ["scripts/install.sh"]
|
|
16
|
+
},
|
|
13
17
|
"agentBinary": "claude"
|
|
14
18
|
}
|
package/src/pragma-plugin.ts
CHANGED
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
type PluginDefinition,
|
|
7
7
|
type UsageLimit,
|
|
8
8
|
type UsageLimitsResult,
|
|
9
|
-
} from "@pragma/plugin/catalog";
|
|
10
|
-
import { createTuiWatcher } from "@pragma/watcher-kit";
|
|
9
|
+
} from "@pragma-sh/plugin/catalog";
|
|
10
|
+
import { createTuiWatcher } from "@pragma-sh/watcher-kit";
|
|
11
11
|
|
|
12
12
|
/** Lets Claude Code's paste-aware TUI commit interjected text before Enter. */
|
|
13
13
|
const INTERJECT_SUBMIT_DELAY_MS = 200;
|