@onlooker-community/ecosystem 0.15.2 → 0.17.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.
Files changed (54) hide show
  1. package/.claude-plugin/marketplace.json +39 -0
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.release-please-manifest.json +5 -2
  4. package/CHANGELOG.md +15 -0
  5. package/CLAUDE.md +88 -0
  6. package/package.json +3 -3
  7. package/plugins/compass/.claude-plugin/plugin.json +14 -0
  8. package/plugins/compass/CHANGELOG.md +8 -0
  9. package/plugins/compass/config.json +71 -0
  10. package/plugins/compass/docs/adr/001-evaluate-prompts-in-context.md +82 -0
  11. package/plugins/compass/docs/design.md +421 -0
  12. package/plugins/compass/hooks/hooks.json +82 -0
  13. package/plugins/compass/scripts/hooks/compass-bash-gate.sh +95 -0
  14. package/plugins/compass/scripts/hooks/compass-pre-tool-use.sh +86 -0
  15. package/plugins/compass/scripts/hooks/compass-record-write.sh +97 -0
  16. package/plugins/compass/scripts/hooks/compass-session-start.sh +77 -0
  17. package/plugins/compass/scripts/lib/compass-config.sh +72 -0
  18. package/plugins/compass/scripts/lib/compass-evaluator.sh +374 -0
  19. package/plugins/compass/scripts/lib/compass-events.sh +81 -0
  20. package/plugins/compass/scripts/lib/compass-gate.sh +465 -0
  21. package/plugins/compass/scripts/lib/compass-sanitizer.sh +82 -0
  22. package/plugins/compass/scripts/lib/compass-transcript.sh +135 -0
  23. package/plugins/governor/.claude-plugin/plugin.json +14 -0
  24. package/plugins/governor/CHANGELOG.md +22 -0
  25. package/plugins/governor/config.json +19 -0
  26. package/plugins/governor/hooks/hooks.json +48 -0
  27. package/plugins/governor/scripts/hooks/governor-post-tool-use.sh +147 -0
  28. package/plugins/governor/scripts/hooks/governor-pre-tool-use.sh +199 -0
  29. package/plugins/governor/scripts/hooks/governor-session-start.sh +109 -0
  30. package/plugins/governor/scripts/hooks/governor-stop.sh +108 -0
  31. package/plugins/governor/scripts/lib/governor-config.sh +79 -0
  32. package/plugins/governor/scripts/lib/governor-estimate.sh +116 -0
  33. package/plugins/governor/scripts/lib/governor-events.sh +81 -0
  34. package/plugins/governor/scripts/lib/governor-ledger.sh +172 -0
  35. package/plugins/scribe/.claude-plugin/plugin.json +12 -0
  36. package/plugins/scribe/CHANGELOG.md +8 -0
  37. package/plugins/scribe/config.json +20 -0
  38. package/plugins/scribe/hooks/hooks.json +37 -0
  39. package/plugins/scribe/scripts/hooks/scribe-capture.sh +76 -0
  40. package/plugins/scribe/scripts/hooks/scribe-session-start.sh +58 -0
  41. package/plugins/scribe/scripts/hooks/scribe-stop.sh +67 -0
  42. package/plugins/scribe/scripts/lib/scribe-config.sh +72 -0
  43. package/plugins/scribe/scripts/lib/scribe-distill.sh +239 -0
  44. package/plugins/scribe/scripts/lib/scribe-events.sh +80 -0
  45. package/plugins/scribe/scripts/lib/scribe-extract.sh +147 -0
  46. package/plugins/scribe/scripts/lib/scribe-project-key.sh +89 -0
  47. package/plugins/scribe/scripts/lib/scribe-ulid.sh +50 -0
  48. package/release-please-config.json +48 -0
  49. package/test/bats/governor-config.bats +106 -0
  50. package/test/bats/governor-estimate.bats +86 -0
  51. package/test/bats/governor-events.bats +238 -0
  52. package/test/bats/governor-ledger.bats +220 -0
  53. package/test/bats/scribe-extract.bats +102 -0
  54. package/test/bats/scribe-project-key.bats +75 -0
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env bats
2
+
3
+ setup() {
4
+ source "${BATS_TEST_DIRNAME}/../helpers/setup.bash"
5
+ setup_test_env
6
+
7
+ PLUGIN_ROOT="${REPO_ROOT}/plugins/governor"
8
+ export CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT"
9
+ export ONLOOKER_EVENTS_LOG="${ONLOOKER_DIR}/logs/onlooker-events.jsonl"
10
+ mkdir -p "$(dirname "$ONLOOKER_EVENTS_LOG")"
11
+
12
+ export _ONLOOKER_EVENT_JS="${REPO_ROOT}/scripts/lib/onlooker-event.mjs"
13
+ export CLAUDE_SESSION_ID="bats-ledger-$$"
14
+
15
+ # shellcheck disable=SC1091
16
+ source "${REPO_ROOT}/scripts/lib/portable-lock.sh"
17
+ # shellcheck disable=SC1091
18
+ source "${PLUGIN_ROOT}/scripts/lib/governor-config.sh"
19
+ # shellcheck disable=SC1091
20
+ source "${PLUGIN_ROOT}/scripts/lib/governor-events.sh"
21
+ # shellcheck disable=SC1091
22
+ source "${PLUGIN_ROOT}/scripts/lib/governor-ledger.sh"
23
+
24
+ export ONLOOKER_DIR="${TEST_HOME}/.onlooker"
25
+ SID="ledger-test-session-$$"
26
+ }
27
+
28
+ _make_record() {
29
+ local tokens="${1:-1000}"
30
+ local cost="${2:-0.009}"
31
+ jq -n \
32
+ --arg ts "2026-01-01T00:00:00Z" \
33
+ --arg sid "$SID" \
34
+ --arg aid "test-agent" \
35
+ --argjson est "$tokens" \
36
+ --argjson cost "$cost" \
37
+ '{
38
+ ts: $ts,
39
+ session_id: $sid,
40
+ agent_id: $aid,
41
+ agent_type: "Task",
42
+ estimated_tokens: $est,
43
+ cost_usd_estimated: $cost,
44
+ duration_ms: 1000
45
+ }'
46
+ }
47
+
48
+ _make_reservation() {
49
+ local tokens="${1:-1000}"
50
+ local cost="${2:-0.009}"
51
+ jq -n \
52
+ --arg ts "2026-01-01T00:00:00Z" \
53
+ --arg sid "$SID" \
54
+ --arg aid "test-agent" \
55
+ --argjson est "$tokens" \
56
+ --argjson cost "$cost" \
57
+ '{
58
+ ts: $ts,
59
+ session_id: $sid,
60
+ agent_id: $aid,
61
+ agent_type: "Task",
62
+ estimated_tokens: $est,
63
+ cost_usd_estimated: $cost,
64
+ record_type: "reservation"
65
+ }'
66
+ }
67
+
68
+ _make_completion() {
69
+ local neg_tokens="${1:--1000}"
70
+ local actual_tokens="${2:-900}"
71
+ local cost="${3:-0.009}"
72
+ jq -n \
73
+ --arg ts "2026-01-01T00:00:00Z" \
74
+ --arg sid "$SID" \
75
+ --arg aid "test-agent" \
76
+ --argjson est "$neg_tokens" \
77
+ --argjson actual "$actual_tokens" \
78
+ --argjson cost "$cost" \
79
+ '{
80
+ ts: $ts,
81
+ session_id: $sid,
82
+ agent_id: $aid,
83
+ agent_type: "Task",
84
+ estimated_tokens: $est,
85
+ actual_tokens: $actual,
86
+ cost_usd_estimated: $cost,
87
+ duration_ms: 1500
88
+ }'
89
+ }
90
+
91
+ @test "governor_ledger_path returns a .jsonl path under ONLOOKER_DIR" {
92
+ local p
93
+ p=$(governor_ledger_path "test-session")
94
+ [[ "$p" == *".jsonl" ]]
95
+ [[ "$p" == *"test-session"* ]]
96
+ }
97
+
98
+ @test "governor_ledger_total_tokens returns 0 for missing ledger" {
99
+ local total
100
+ total=$(governor_ledger_total_tokens "no-such-session")
101
+ [ "$total" = "0" ]
102
+ }
103
+
104
+ @test "governor_ledger_total_cost returns 0 for missing ledger" {
105
+ local total
106
+ total=$(governor_ledger_total_cost "no-such-session")
107
+ [ "$total" = "0" ]
108
+ }
109
+
110
+ @test "governor_ledger_call_count returns 0 for missing ledger" {
111
+ local count
112
+ count=$(governor_ledger_call_count "no-such-session")
113
+ [ "$count" = "0" ]
114
+ }
115
+
116
+ @test "governor_ledger_append writes a record and it is readable" {
117
+ local record
118
+ record=$(_make_record 5000 0.045)
119
+ governor_ledger_append "$SID" "$record"
120
+
121
+ local ledger_path
122
+ ledger_path=$(governor_ledger_path "$SID")
123
+ [ -f "$ledger_path" ]
124
+
125
+ local lines
126
+ lines=$(awk 'END{print NR}' "$ledger_path")
127
+ [ "$lines" = "1" ]
128
+ }
129
+
130
+ @test "governor_ledger_total_tokens sums across multiple records" {
131
+ governor_ledger_append "$SID" "$(_make_record 3000 0.027)"
132
+ governor_ledger_append "$SID" "$(_make_record 4000 0.036)"
133
+ governor_ledger_append "$SID" "$(_make_record 2500 0.023)"
134
+
135
+ local total
136
+ total=$(governor_ledger_total_tokens "$SID")
137
+ [ "$total" = "9500" ]
138
+ }
139
+
140
+ @test "governor_ledger_total_cost sums across multiple records" {
141
+ governor_ledger_append "$SID" "$(_make_record 1000 0.01)"
142
+ governor_ledger_append "$SID" "$(_make_record 1000 0.02)"
143
+
144
+ local total
145
+ total=$(governor_ledger_total_cost "$SID")
146
+ # Allow slight floating-point representation variance
147
+ [[ "$total" =~ ^0\.03 ]]
148
+ }
149
+
150
+ @test "governor_ledger_call_count counts records" {
151
+ governor_ledger_append "$SID" "$(_make_record 1000 0.009)"
152
+ governor_ledger_append "$SID" "$(_make_record 1000 0.009)"
153
+ governor_ledger_append "$SID" "$(_make_record 1000 0.009)"
154
+
155
+ local count
156
+ count=$(governor_ledger_call_count "$SID")
157
+ [ "$count" = "3" ]
158
+ }
159
+
160
+ @test "governor_ledger_is_poisoned returns false for healthy ledger" {
161
+ governor_ledger_append "$SID" "$(_make_record 1000 0.009)"
162
+ run governor_ledger_is_poisoned "$SID"
163
+ [ "$status" -ne 0 ]
164
+ }
165
+
166
+ @test "governor_ledger_is_poisoned returns true after poison sentinel" {
167
+ local ledger_path
168
+ ledger_path=$(governor_ledger_path "$SID")
169
+ mkdir -p "$(dirname "$ledger_path")"
170
+ touch "$(governor_ledger_poison_path "$ledger_path")"
171
+
172
+ run governor_ledger_is_poisoned "$SID"
173
+ [ "$status" -eq 0 ]
174
+ }
175
+
176
+ @test "governor_ledger_write_direct writes a record without acquiring the write lock" {
177
+ local ledger_path
178
+ ledger_path=$(governor_ledger_path "$SID")
179
+ mkdir -p "$(dirname "$ledger_path")"
180
+
181
+ local record
182
+ record=$(_make_reservation 2000 0.018)
183
+ governor_ledger_write_direct "$ledger_path" "$record"
184
+
185
+ local lines
186
+ lines=$(awk 'END{print NR}' "$ledger_path")
187
+ [ "$lines" = "1" ]
188
+ }
189
+
190
+ @test "two-phase: reservation plus completion converges to actual tokens" {
191
+ # Phase 1: PreToolUse writes reservation with N_est = 5000
192
+ local reservation
193
+ reservation=$(_make_reservation 5000 0.045)
194
+ governor_ledger_append "$SID" "$reservation"
195
+
196
+ local mid_total
197
+ mid_total=$(governor_ledger_total_tokens "$SID")
198
+ [ "$mid_total" = "5000" ]
199
+
200
+ # Phase 2: PostToolUse writes completion with estimated=-5000, actual=4200
201
+ local completion
202
+ completion=$(_make_completion -5000 4200 0.045)
203
+ governor_ledger_append "$SID" "$completion"
204
+
205
+ # Net: 5000 + (-5000) + 4200 = 4200
206
+ local final_total
207
+ final_total=$(governor_ledger_total_tokens "$SID")
208
+ [ "$final_total" = "4200" ]
209
+ }
210
+
211
+ @test "governor_ledger_call_count excludes reservation records" {
212
+ governor_ledger_append "$SID" "$(_make_reservation 1000 0.009)"
213
+ governor_ledger_append "$SID" "$(_make_completion -1000 900 0.009)"
214
+ governor_ledger_append "$SID" "$(_make_completion -1000 800 0.009)"
215
+
216
+ local count
217
+ count=$(governor_ledger_call_count "$SID")
218
+ # 2 completions, 1 reservation — only completions count
219
+ [ "$count" = "2" ]
220
+ }
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env bats
2
+
3
+ setup() {
4
+ # shellcheck source=../helpers/setup.bash
5
+ source "${BATS_TEST_DIRNAME}/../helpers/setup.bash"
6
+ setup_test_env
7
+
8
+ PLUGIN_ROOT="${REPO_ROOT}/plugins/scribe"
9
+ export CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT"
10
+ # shellcheck disable=SC1091
11
+ source "${PLUGIN_ROOT}/scripts/lib/scribe-config.sh"
12
+ # shellcheck disable=SC1091
13
+ source "${PLUGIN_ROOT}/scripts/lib/scribe-extract.sh"
14
+ }
15
+
16
+ # ---------------------------------------------------------------------------
17
+ # scribe_count_turns
18
+ # ---------------------------------------------------------------------------
19
+
20
+ @test "count_turns returns 0 for missing file" {
21
+ run scribe_count_turns "/nonexistent/path.jsonl"
22
+ [ "$status" -eq 0 ]
23
+ [ "$output" = "0" ]
24
+ }
25
+
26
+ @test "count_turns returns 0 for empty file" {
27
+ local f="${BATS_TEST_TMPDIR}/empty.jsonl"
28
+ touch "$f"
29
+ run scribe_count_turns "$f"
30
+ [ "$status" -eq 0 ]
31
+ [ "$output" = "0" ]
32
+ }
33
+
34
+ @test "count_turns counts only user-role entries" {
35
+ local f="${BATS_TEST_TMPDIR}/transcript.jsonl"
36
+ printf '%s\n' \
37
+ '{"role":"user","content":"hello"}' \
38
+ '{"role":"assistant","content":"hi"}' \
39
+ '{"role":"user","content":"thanks"}' \
40
+ '{"role":"assistant","content":"sure"}' \
41
+ > "$f"
42
+ run scribe_count_turns "$f"
43
+ [ "$status" -eq 0 ]
44
+ [ "$output" = "2" ]
45
+ }
46
+
47
+ @test "count_turns skips blank lines" {
48
+ local f="${BATS_TEST_TMPDIR}/sparse.jsonl"
49
+ printf '%s\n' \
50
+ '{"role":"user","content":"a"}' \
51
+ '' \
52
+ '{"role":"user","content":"b"}' \
53
+ > "$f"
54
+ run scribe_count_turns "$f"
55
+ [ "$status" -eq 0 ]
56
+ [ "$output" = "2" ]
57
+ }
58
+
59
+ # ---------------------------------------------------------------------------
60
+ # min_turns gate (via scribe_distill return code)
61
+ # ---------------------------------------------------------------------------
62
+
63
+ @test "scribe_distill exits 2 when transcript has fewer turns than min_turns" {
64
+ # shellcheck disable=SC1091
65
+ source "${PLUGIN_ROOT}/scripts/lib/scribe-events.sh"
66
+ # shellcheck disable=SC1091
67
+ source "${PLUGIN_ROOT}/scripts/lib/scribe-project-key.sh"
68
+ # shellcheck disable=SC1091
69
+ source "${PLUGIN_ROOT}/scripts/lib/scribe-distill.sh"
70
+
71
+ # Load config so min_turns is available (default: 3).
72
+ scribe_config_load ""
73
+
74
+ # Transcript with only 1 user turn — below default min_turns of 3.
75
+ local f="${BATS_TEST_TMPDIR}/short.jsonl"
76
+ printf '%s\n' \
77
+ '{"role":"user","content":"hey"}' \
78
+ '{"role":"assistant","content":"yo"}' \
79
+ > "$f"
80
+
81
+ run scribe_distill "test-session-id" "${BATS_TEST_TMPDIR}" "$f"
82
+ [ "$status" -eq 2 ]
83
+ }
84
+
85
+ @test "scribe_distill min_turns skip produces no stderr output" {
86
+ # shellcheck disable=SC1091
87
+ source "${PLUGIN_ROOT}/scripts/lib/scribe-events.sh"
88
+ # shellcheck disable=SC1091
89
+ source "${PLUGIN_ROOT}/scripts/lib/scribe-project-key.sh"
90
+ # shellcheck disable=SC1091
91
+ source "${PLUGIN_ROOT}/scripts/lib/scribe-distill.sh"
92
+
93
+ scribe_config_load ""
94
+
95
+ local f="${BATS_TEST_TMPDIR}/short2.jsonl"
96
+ printf '%s\n' '{"role":"user","content":"hi"}' > "$f"
97
+
98
+ # Run in a subshell capturing stderr separately.
99
+ local stderr_out
100
+ stderr_out=$(scribe_distill "test-sid" "${BATS_TEST_TMPDIR}" "$f" 2>&1 >/dev/null) || true
101
+ [ -z "$stderr_out" ]
102
+ }
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env bats
2
+
3
+ setup() {
4
+ # shellcheck source=../helpers/setup.bash
5
+ source "${BATS_TEST_DIRNAME}/../helpers/setup.bash"
6
+ setup_test_env
7
+
8
+ PLUGIN_ROOT="${REPO_ROOT}/plugins/scribe"
9
+ # shellcheck disable=SC1091
10
+ source "${PLUGIN_ROOT}/scripts/lib/scribe-project-key.sh"
11
+ }
12
+
13
+ @test "non-git directory returns empty key" {
14
+ local d="${BATS_TEST_TMPDIR}/non-git"
15
+ mkdir -p "$d"
16
+ run scribe_project_key "$d"
17
+ [ "$status" -eq 0 ]
18
+ [ -z "$output" ]
19
+ }
20
+
21
+ @test "git repo without remote falls back to repo-root hash" {
22
+ local d="${BATS_TEST_TMPDIR}/local-only-repo"
23
+ mkdir -p "$d"
24
+ git -C "$d" init -q
25
+ git -C "$d" config user.email t@example.com
26
+ git -C "$d" config user.name "Test"
27
+
28
+ local k1
29
+ k1=$(scribe_project_key "$d")
30
+ [ -n "$k1" ]
31
+ [ "${#k1}" -eq 12 ]
32
+
33
+ # Stability: a second call returns the same key.
34
+ local k2
35
+ k2=$(scribe_project_key "$d")
36
+ [ "$k1" = "$k2" ]
37
+ }
38
+
39
+ @test "git repo with remote uses remote hash, ignores local path" {
40
+ local a="${BATS_TEST_TMPDIR}/clone-a"
41
+ local b="${BATS_TEST_TMPDIR}/clone-b"
42
+ mkdir -p "$a" "$b"
43
+ for d in "$a" "$b"; do
44
+ git -C "$d" init -q
45
+ git -C "$d" config user.email t@example.com
46
+ git -C "$d" config user.name "Test"
47
+ git -C "$d" remote add origin git@github.com:org/proj.git
48
+ done
49
+
50
+ local ka kb
51
+ ka=$(scribe_project_key "$a")
52
+ kb=$(scribe_project_key "$b")
53
+ [ -n "$ka" ]
54
+ [ "$ka" = "$kb" ]
55
+ }
56
+
57
+ @test "different remotes yield different keys" {
58
+ local a="${BATS_TEST_TMPDIR}/proj-a"
59
+ local b="${BATS_TEST_TMPDIR}/proj-b"
60
+ mkdir -p "$a" "$b"
61
+ for d in "$a" "$b"; do
62
+ git -C "$d" init -q
63
+ git -C "$d" config user.email t@example.com
64
+ git -C "$d" config user.name "Test"
65
+ done
66
+ git -C "$a" remote add origin git@github.com:org/proj-a.git
67
+ git -C "$b" remote add origin git@github.com:org/proj-b.git
68
+
69
+ local ka kb
70
+ ka=$(scribe_project_key "$a")
71
+ kb=$(scribe_project_key "$b")
72
+ [ -n "$ka" ]
73
+ [ -n "$kb" ]
74
+ [ "$ka" != "$kb" ]
75
+ }