@onlooker-community/ecosystem 0.16.0 → 0.18.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 +2 -2
  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/counsel/.claude-plugin/plugin.json +14 -0
  24. package/plugins/counsel/CHANGELOG.md +8 -0
  25. package/plugins/counsel/config.json +20 -0
  26. package/plugins/counsel/hooks/hooks.json +15 -0
  27. package/plugins/counsel/scripts/hooks/counsel-session-start.sh +106 -0
  28. package/plugins/counsel/scripts/lib/counsel-brief.sh +247 -0
  29. package/plugins/counsel/scripts/lib/counsel-config.sh +72 -0
  30. package/plugins/counsel/scripts/lib/counsel-events.sh +80 -0
  31. package/plugins/counsel/scripts/lib/counsel-project-key.sh +79 -0
  32. package/plugins/counsel/scripts/lib/counsel-reader.sh +114 -0
  33. package/plugins/counsel/scripts/lib/counsel-synthesize.sh +103 -0
  34. package/plugins/counsel/scripts/lib/counsel-ulid.sh +45 -0
  35. package/plugins/governor/.claude-plugin/plugin.json +1 -1
  36. package/plugins/governor/CHANGELOG.md +7 -0
  37. package/plugins/scribe/.claude-plugin/plugin.json +12 -0
  38. package/plugins/scribe/CHANGELOG.md +8 -0
  39. package/plugins/scribe/config.json +20 -0
  40. package/plugins/scribe/hooks/hooks.json +37 -0
  41. package/plugins/scribe/scripts/hooks/scribe-capture.sh +76 -0
  42. package/plugins/scribe/scripts/hooks/scribe-session-start.sh +58 -0
  43. package/plugins/scribe/scripts/hooks/scribe-stop.sh +67 -0
  44. package/plugins/scribe/scripts/lib/scribe-config.sh +72 -0
  45. package/plugins/scribe/scripts/lib/scribe-distill.sh +239 -0
  46. package/plugins/scribe/scripts/lib/scribe-events.sh +80 -0
  47. package/plugins/scribe/scripts/lib/scribe-extract.sh +147 -0
  48. package/plugins/scribe/scripts/lib/scribe-project-key.sh +89 -0
  49. package/plugins/scribe/scripts/lib/scribe-ulid.sh +50 -0
  50. package/release-please-config.json +48 -0
  51. package/test/bats/counsel-project-key.bats +82 -0
  52. package/test/bats/counsel-reader.bats +132 -0
  53. package/test/bats/scribe-extract.bats +102 -0
  54. package/test/bats/scribe-project-key.bats +75 -0
@@ -0,0 +1,82 @@
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/counsel"
9
+ export CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT"
10
+ # shellcheck disable=SC1091
11
+ source "${PLUGIN_ROOT}/scripts/lib/counsel-project-key.sh"
12
+ }
13
+
14
+ @test "non-git directory returns empty key" {
15
+ local d="${BATS_TEST_TMPDIR}/non-git"
16
+ mkdir -p "$d"
17
+ run counsel_project_key "$d"
18
+ [ "$status" -eq 0 ]
19
+ [ -z "$output" ]
20
+ }
21
+
22
+ @test "git repo without remote falls back to repo-root hash" {
23
+ local d="${BATS_TEST_TMPDIR}/local-only-repo"
24
+ mkdir -p "$d"
25
+ git -C "$d" init -q
26
+ git -C "$d" config user.email t@example.com
27
+ git -C "$d" config user.name "Test"
28
+
29
+ local k1
30
+ k1=$(counsel_project_key "$d")
31
+ [ -n "$k1" ]
32
+ [ "${#k1}" -eq 12 ]
33
+
34
+ local k2
35
+ k2=$(counsel_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=$(counsel_project_key "$a")
52
+ kb=$(counsel_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=$(counsel_project_key "$a")
71
+ kb=$(counsel_project_key "$b")
72
+ [ -n "$ka" ]
73
+ [ -n "$kb" ]
74
+ [ "$ka" != "$kb" ]
75
+ }
76
+
77
+ @test "counsel_project_dir includes project key in path" {
78
+ local key="abc123def456"
79
+ run counsel_project_dir "$key"
80
+ [ "$status" -eq 0 ]
81
+ [[ "$output" == *"counsel/${key}/briefs" ]]
82
+ }
@@ -0,0 +1,132 @@
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/counsel"
9
+ export CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT"
10
+ # shellcheck disable=SC1091
11
+ source "${PLUGIN_ROOT}/scripts/lib/counsel-reader.sh"
12
+ }
13
+
14
+ # ---------------------------------------------------------------------------
15
+ # counsel_count_events
16
+ # ---------------------------------------------------------------------------
17
+
18
+ @test "count_events returns 0 for empty input" {
19
+ run counsel_count_events ""
20
+ [ "$status" -eq 0 ]
21
+ [ "$output" = "0" ]
22
+ }
23
+
24
+ @test "count_events counts non-blank lines" {
25
+ local text
26
+ text=$(printf '%s\n%s\n%s\n' '{"type":"a"}' '{"type":"b"}' '{"type":"c"}')
27
+ run counsel_count_events "$text"
28
+ [ "$status" -eq 0 ]
29
+ [ "$output" = "3" ]
30
+ }
31
+
32
+ # ---------------------------------------------------------------------------
33
+ # counsel_sources_from_events
34
+ # ---------------------------------------------------------------------------
35
+
36
+ @test "sources_from_events returns onlooker_events for unknown types" {
37
+ local text='{"type":"scribe.distill.complete"}'
38
+ run counsel_sources_from_events "$text"
39
+ [ "$status" -eq 0 ]
40
+ [[ "$output" == *"onlooker_events"* ]]
41
+ }
42
+
43
+ @test "sources_from_events detects tribunal events" {
44
+ local text='{"type":"tribunal.gate.blocked"}'
45
+ run counsel_sources_from_events "$text"
46
+ [ "$status" -eq 0 ]
47
+ [[ "$output" == *"tribunal_verdicts"* ]]
48
+ }
49
+
50
+ @test "sources_from_events detects echo events" {
51
+ local text='{"type":"echo.regression.detected"}'
52
+ run counsel_sources_from_events "$text"
53
+ [ "$status" -eq 0 ]
54
+ [[ "$output" == *"echo_regressions"* ]]
55
+ }
56
+
57
+ @test "sources_from_events returns onlooker_events for empty input" {
58
+ run counsel_sources_from_events ""
59
+ [ "$status" -eq 0 ]
60
+ [ "$output" = '["onlooker_events"]' ]
61
+ }
62
+
63
+ # ---------------------------------------------------------------------------
64
+ # counsel_read_events — file-based
65
+ # ---------------------------------------------------------------------------
66
+
67
+ @test "read_events returns empty when log does not exist" {
68
+ export ONLOOKER_EVENTS_LOG="${BATS_TEST_TMPDIR}/no-log.jsonl"
69
+ run counsel_read_events "30" "60000"
70
+ [ "$status" -eq 0 ]
71
+ [ -z "$output" ]
72
+ }
73
+
74
+ @test "read_events returns empty for empty log" {
75
+ local log="${BATS_TEST_TMPDIR}/empty-log.jsonl"
76
+ touch "$log"
77
+ export ONLOOKER_EVENTS_LOG="$log"
78
+ run counsel_read_events "30" "60000"
79
+ [ "$status" -eq 0 ]
80
+ [ -z "$output" ]
81
+ }
82
+
83
+ @test "read_events filters events within lookback window" {
84
+ local log="${BATS_TEST_TMPDIR}/events.jsonl"
85
+ # Use a timestamp far in the future to ensure it passes any lookback filter.
86
+ local ts
87
+ ts=$(date -u '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null) || ts="2099-01-01T00:00:00Z"
88
+ printf '%s\n' \
89
+ "{\"event_type\":\"scribe.distill.complete\",\"timestamp\":\"${ts}\",\"session_id\":\"s1\",\"payload\":{}}" \
90
+ > "$log"
91
+ export ONLOOKER_EVENTS_LOG="$log"
92
+ run counsel_read_events "30" "60000"
93
+ [ "$status" -eq 0 ]
94
+ [[ "$output" == *"scribe.distill.complete"* ]]
95
+ }
96
+
97
+ @test "read_events output is JSONL-shaped: one object per line" {
98
+ local log="${BATS_TEST_TMPDIR}/multi-events.jsonl"
99
+ local ts
100
+ ts=$(date -u '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null) || ts="2099-01-01T00:00:00Z"
101
+ printf '%s\n' \
102
+ "{\"event_type\":\"tribunal.gate.blocked\",\"timestamp\":\"${ts}\",\"session_id\":\"s1\",\"payload\":{}}" \
103
+ "{\"event_type\":\"echo.regression.detected\",\"timestamp\":\"${ts}\",\"session_id\":\"s2\",\"payload\":{}}" \
104
+ "{\"event_type\":\"scribe.distill.complete\",\"timestamp\":\"${ts}\",\"session_id\":\"s3\",\"payload\":{}}" \
105
+ > "$log"
106
+ export ONLOOKER_EVENTS_LOG="$log"
107
+
108
+ local events
109
+ events=$(counsel_read_events "30" "60000")
110
+
111
+ # count_events must see exactly 3 records, not inflated by pretty-printing.
112
+ run counsel_count_events "$events"
113
+ [ "$status" -eq 0 ]
114
+ [ "$output" = "3" ]
115
+ }
116
+
117
+ @test "read_events output preserves source types for sources_from_events" {
118
+ local log="${BATS_TEST_TMPDIR}/source-events.jsonl"
119
+ local ts
120
+ ts=$(date -u '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null) || ts="2099-01-01T00:00:00Z"
121
+ printf '%s\n' \
122
+ "{\"event_type\":\"tribunal.gate.blocked\",\"timestamp\":\"${ts}\",\"session_id\":\"s1\",\"payload\":{}}" \
123
+ > "$log"
124
+ export ONLOOKER_EVENTS_LOG="$log"
125
+
126
+ local events
127
+ events=$(counsel_read_events "30" "60000")
128
+
129
+ run counsel_sources_from_events "$events"
130
+ [ "$status" -eq 0 ]
131
+ [[ "$output" == *"tribunal_verdicts"* ]]
132
+ }
@@ -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
+ }